diff --git a/.gitignore b/.gitignore index 140c0b8bb4..8359917213 100644 --- a/.gitignore +++ b/.gitignore @@ -62,7 +62,6 @@ libraries/jsonball/gensrc/ libraries/jsonball/template_context/ libraries/manifest/gensrc/ libraries/manifest/template_context/ -libraries/mira/test/mira_test libraries/utilities/git_revision.cpp libraries/vendor/rocksdb/RocksDBConfig.cmake libraries/vendor/rocksdb/RocksDBConfigVersion.cmake diff --git a/.gitlab-ci.yaml b/.gitlab-ci.yaml index 7179db3cba..e11d3733a3 100644 --- a/.gitlab-ci.yaml +++ b/.gitlab-ci.yaml @@ -15,8 +15,8 @@ testnet_node_build: stage: build image: "$CI_REGISTRY_IMAGE/builder$BUILDER_IMAGE_TAG" script: - # LOW_MEMORY=OFF CLEAR_VOTES=OFF TESTNET=ON ENABLE_MIRA=OFF - - ./ciscripts/build.sh OFF OFF ON OFF + # TESTNET=ON HIVE_LINT=ON + - ./ciscripts/build.sh ON ON - mkdir -p "$CI_JOB_NAME"/tests/unit - mv build/install-root "$CI_JOB_NAME" - mv contrib/hived.run "$CI_JOB_NAME" @@ -35,8 +35,8 @@ consensus_build: stage: build image: "$CI_REGISTRY_IMAGE/builder$BUILDER_IMAGE_TAG" script: - # LOW_MEMORY=ON CLEAR_VOTES=ON TESTNET=OFF ENABLE_MIRA=OFF - - ./ciscripts/build.sh ON ON OFF OFF + # TESTNET=OFF HIVE_LINT=ON + - ./ciscripts/build.sh OFF ON - mkdir "$CI_JOB_NAME" - mv build/install-root "$CI_JOB_NAME" - mv contrib/hived.run "$CI_JOB_NAME" @@ -86,6 +86,15 @@ plugin_test: tags: - public-runner-docker +.beem_setup : &beem_setup | + git clone --depth=1 --single-branch --branch master https://gitlab.syncad.com/hive/beem.git + cd beem + python3 -m pip install --upgrade pip + python3 -m pip install --upgrade -r requirements-test.txt + python3 setup.py install + cd .. + mkdir -p build/tests/hive-node-data + beem_tests: stage: test needs: @@ -95,13 +104,7 @@ beem_tests: variables: PYTHONPATH: $CI_PROJECT_DIR/tests/functional script: - # boilerplate for installing latested beem - - git clone --depth=1 --single-branch --branch dk-hybrid-operations https://gitlab.syncad.com/hive/beem.git - - cd beem - - python3 setup.py install - - cd .. - # stuff specific to this test - - mkdir -p build/tests/hive-node-data + - *beem_setup - cd tests/functional/python_tests/dhf_tests - "python3 run_proposal_tests.py initminer hive.fund 5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n --run-hived $CI_PROJECT_DIR/testnet_node_build/install-root/bin/hived --working-dir=$CI_PROJECT_DIR/build/tests/hive-node-data" - rm -rf $CI_PROJECT_DIR/build/tests/hive-node-data @@ -130,13 +133,7 @@ list_proposals_tests: variables: PYTHONPATH: $CI_PROJECT_DIR/tests/functional script: - # boilerplate for installing latested beem - - git clone --depth=1 --single-branch --branch dk-hybrid-operations https://gitlab.syncad.com/hive/beem.git - - cd beem - - python3 setup.py install - - cd .. - # stuff specific to this test - - mkdir -p build/tests/hive-node-data + - *beem_setup - cd tests/functional/python_tests/dhf_tests - "python3 list_proposals_tests.py initminer initminer 5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n --run-hived $CI_PROJECT_DIR/testnet_node_build/install-root/bin/hived --working-dir=$CI_PROJECT_DIR/build/tests/hive-node-data --junit-output=list_proposals_tests.xml" artifacts: @@ -149,6 +146,28 @@ list_proposals_tests: tags: - public-runner-docker +cli_wallet_tests: + stage: test + needs: + - job: testnet_node_build + artifacts: true + image: "$CI_REGISTRY_IMAGE/test$TEST_IMAGE_TAG" + variables: + PYTHONPATH: $CI_PROJECT_DIR/tests/functional + script: + - *beem_setup + - cd tests/functional/python_tests/cli_wallet + - "python3 run.py --hive-path $CI_PROJECT_DIR/testnet_node_build/install-root/bin/hived --hive-working-dir=$CI_PROJECT_DIR/build/tests/hive-node-data --path-to-cli $CI_PROJECT_DIR/testnet_node_build/install-root/bin --creator initminer --wif 5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n --junit-output=cli_wallet_tests.xml" + artifacts: + paths: + - tests/functional/python_tests/cli_wallet/tests/logs/cli_wallet.log + reports: + junit: tests/functional/python_tests/cli_wallet/cli_wallet_tests.xml + when: always + expire_in: 6 months + tags: + - public-runner-docker + hived_options_tests: stage: test needs: @@ -159,10 +178,97 @@ hived_options_tests: PYTHONPATH: $CI_PROJECT_DIR/tests/functional script: - cd tests/functional/python_tests/hived + - apt-get update -y && apt-get install -y python3 python3-pip python3-dev + - pip3 install -U psutil - "python3 hived_options_tests.py --run-hived $CI_PROJECT_DIR/testnet_node_build/install-root/bin/hived" tags: - public-runner-docker +.test_tools_based: + variables: + PYTHONPATH: "$CI_PROJECT_DIR/tests/test_tools/package" + HIVED_PATH: "$CI_PROJECT_DIR/testnet_node_build/install-root/bin/hived" + CLI_WALLET_PATH: "$CI_PROJECT_DIR/testnet_node_build/install-root/bin/cli_wallet" + GET_DEV_KEY_PATH: "$CI_PROJECT_DIR/testnet_node_build/install-root/bin/get_dev_key" + before_script: + - apt-get update -y && apt-get install -y python3 python3-pip python3-dev + - pip3 install requests + +create_account_tests: + stage: test + extends: .test_tools_based + needs: + - job: testnet_node_build + artifacts: true + image: "$CI_REGISTRY_IMAGE/test$TEST_IMAGE_TAG" + script: + - pip3 install pytest + - cd tests/functional/python_tests/create_account_tests + - pytest + tags: + - public-runner-docker + +last_irreversible_block_tests: + stage: test + extends: .test_tools_based + needs: + - job: testnet_node_build + artifacts: true + image: "$CI_REGISTRY_IMAGE/test$TEST_IMAGE_TAG" + script: + - pip3 install pytest pytest-timeout + - cd tests/functional/python_tests/last_irreversible_block_tests + - pytest + tags: + - public-runner-docker + +test_tools_tests: + stage: test + extends: .test_tools_based + needs: + - job: testnet_node_build + artifacts: true + image: "$CI_REGISTRY_IMAGE/test$TEST_IMAGE_TAG" + script: + - pip3 install pytest + - cd tests/test_tools/tests + - pytest + tags: + - public-runner-docker + +hived_replay_tests: + stage: test + needs: + - job: consensus_build + artifacts: true + image: "$CI_REGISTRY_IMAGE/builder$BUILDER_IMAGE_TAG" + variables: + PYTHONPATH: $CI_PROJECT_DIR/tests/functional + script: + + - export ROOT_DIRECTORY=$PWD + - mkdir $ROOT_DIRECTORY/replay_logs + - cd tests/functional/python_tests/hived + - apt-get update -y && apt-get install -y python3 python3-pip python3-dev + - pip3 install -U wget psutil junit_xml gcovr secp256k1prp requests + - $CI_PROJECT_DIR/consensus_build/install-root/bin/truncate_block_log /blockchain/block_log /tmp/block_log 3000000 + + # quick replays for 10k blocks, with node restarts + - "python3 snapshot_1.py --run-hived $CI_PROJECT_DIR/consensus_build/install-root/bin/hived --block-log /tmp/block_log --blocks 10000 --artifact-directory $ROOT_DIRECTORY/replay_logs" + - "python3 snapshot_2.py --run-hived $CI_PROJECT_DIR/consensus_build/install-root/bin/hived --block-log /tmp/block_log --blocks 10000 --artifact-directory $ROOT_DIRECTORY/replay_logs" + + # group of tests, that uses one node with 5 milion blocks replayed + - "python3 start_replay_tests.py --run-hived $CI_PROJECT_DIR/consensus_build/install-root/bin/hived --blocks 3000000 --block-log /tmp/block_log --test-directory $PWD/replay_based_tests --artifact-directory $ROOT_DIRECTORY/replay_logs" + + artifacts: + paths: + - replay_logs + when: always + expire_in: 6 months + tags: + - public-runner-docker + - hived-for-tests + package_consensus_node: stage: package needs: @@ -182,3 +288,4 @@ package_consensus_node: - "echo ===> the consensus node image for this build is: $CI_REGISTRY_IMAGE/consensus_node:$CI_COMMIT_SHORT_SHA" tags: - public-runner-docker + diff --git a/.gitmodules b/.gitmodules index 40c70c8616..01aac50ad2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "libraries/fc/vendor/diff-match-patch-cpp-stl"] - path = libraries/fc/vendor/diff-match-patch-cpp-stl - url = https://github.com/leutloff/diff-match-patch-cpp-stl.git [submodule "libraries/fc/vendor/secp256k1-zkp"] path = libraries/fc/vendor/secp256k1-zkp url = https://github.com/cryptonomex/secp256k1-zkp.git @@ -11,3 +8,6 @@ path = tests/tests_api url = https://gitlab.syncad.com/hive/tests_api.git branch = mi_tests_import +[submodule "tests/test_tools"] + path = tests/test_tools + url = https://gitlab.syncad.com/hive/test-tools.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ee16abeef..0392acb178 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,23 +3,18 @@ project( Hive ) cmake_minimum_required( VERSION 3.2 ) set( BLOCKCHAIN_NAME "Hive" ) + set( CMAKE_CXX_STANDARD 14 ) +set( CMAKE_CXX_EXTENSIONS OFF ) +set( CMAKE_CXX_STANDARD_REQUIRED ON ) + +set( CMAKE_C_STANDARD 99 ) +set( CMAKE_C_STANDARD_REQUIRED ON ) set( GUI_CLIENT_EXECUTABLE_NAME Hive ) set( CUSTOM_URL_SCHEME "gcs" ) set( INSTALLER_APP_ID "68ad7005-8eee-49c9-95ce-9eed97e5b347" ) -# http://stackoverflow.com/a/18369825 -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8) - message(FATAL_ERROR "GCC version must be at least 4.8!") - endif() -elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3) - message(FATAL_ERROR "Clang version must be at least 3.3!") - endif() -endif() - list( APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules" ) set(CMAKE_EXPORT_COMPILE_COMMANDS "ON") @@ -74,27 +69,25 @@ OPTION( ENABLE_MIRA "Build source with MIRA (ON OR OFF)" OFF ) MESSAGE( STATUS "ENABLE_MIRA: ${ENABLE_MIRA}" ) if( ENABLE_MIRA ) MESSAGE( STATUS " " ) - MESSAGE( STATUS " CONFIGURING FOR MIRA " ) + MESSAGE( FATAL_ERROR " NOTE: MIRA HAS BEEN REMOVED " ) MESSAGE( STATUS " " ) - SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DENABLE_MIRA" ) - SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_MIRA" ) endif() -OPTION( LOW_MEMORY_NODE "Build source for low memory node (ON OR OFF)" OFF ) +OPTION( LOW_MEMORY_NODE "Build source for low memory node (ON OR OFF)" ON ) MESSAGE( STATUS "LOW_MEMORY_NODE: ${LOW_MEMORY_NODE}" ) +MESSAGE( STATUS " " ) if( LOW_MEMORY_NODE ) - MESSAGE( STATUS " " ) - MESSAGE( STATUS " CONFIGURING FOR LOW MEMORY NODE " ) - MESSAGE( STATUS " " ) - SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DIS_LOW_MEM" ) - SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DIS_LOW_MEM" ) + MESSAGE( WARNING " NOTE: LOW MEMORY NODE OPTION HAS BEEN DEPRECATED " ) +else() + MESSAGE( FATAL_ERROR " NOTE: LOW MEMORY NODE OPTION HAS BEEN DEPRECATED " ) endif() +MESSAGE( STATUS " " ) -OPTION (COLLECT_ACCOUNT_METADATA "Allows to enable/disable storing account metadata" ON) -MESSAGE( STATUS "COLLECT_ACCOUNT_METADATA: ${COLLECT_ACCOUNT_METADATA}" ) -if( COLLECT_ACCOUNT_METADATA ) +OPTION( STORE_ACCOUNT_METADATA "Keep the json_metadata for accounts (ON OR OFF)" ON ) +MESSAGE( STATUS "STORE_ACCOUNT_METADATA: ${STORE_ACCOUNT_METADATA}" ) +if( STORE_ACCOUNT_METADATA ) MESSAGE( STATUS " " ) - MESSAGE( STATUS " CONFIGURING FOR ACCOUNT METADATA SUPPORT " ) + MESSAGE( STATUS " CONFIGURING TO INDEX ACCOUNT METADATA " ) MESSAGE( STATUS " " ) SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCOLLECT_ACCOUNT_METADATA" ) SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCOLLECT_ACCOUNT_METADATA" ) @@ -103,17 +96,9 @@ endif() OPTION( SUPPORT_COMMENT_CONTENT "Build source with enabled comment content support (ON OR OFF)" OFF ) MESSAGE( STATUS "SUPPORT_COMMENT_CONTENT: ${SUPPORT_COMMENT_CONTENT}" ) IF (SUPPORT_COMMENT_CONTENT) - if( LOW_MEMORY_NODE ) - MESSAGE( WARNING "Ignoring SUPPORT_COMMENT_CONTENT setting due to enabled LOW_MEMORY_NODE") - ELSE() - MESSAGE( STATUS " " ) - MESSAGE( STATUS " CONFIGURING FOR COMMENT_CONTENT_SUPPORT " ) - MESSAGE( STATUS " " ) - SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTORE_COMMENT_CONTENT" ) - SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSTORE_COMMENT_CONTENT" ) - endif() -ELSE() - MESSAGE( STATUS " CONFIGURING to skip COMMENT_CONTENT objects " ) + MESSAGE( STATUS " " ) + MESSAGE( FATAL_ERROR " COMMENT_CONTENT_SUPPORT HAS BEEN REMOVED! " ) + MESSAGE( STATUS " " ) ENDIF() OPTION( CHAINBASE_CHECK_LOCKING "Check locks in chainbase (ON or OFF)" ON ) @@ -126,8 +111,8 @@ endif() OPTION( CLEAR_VOTES "Build source to clear old votes from memory" ON ) if( CLEAR_VOTES ) MESSAGE( STATUS " CONFIGURING TO CLEAR OLD VOTES FROM MEMORY" ) - SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCLEAR_VOTES" ) - SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DCLEAR_VOTES" ) +else() + MESSAGE( FATAL_ERROR " NOTE: CLEAR OLD VOTES OPTION HAS BEEN DEPRECATED" ) endif() OPTION( SKIP_BY_TX_ID "Skip ordering operation history by transaction id (ON or OFF)" OFF ) @@ -144,31 +129,50 @@ if( HIVE_STATIC_BUILD AND ( ( MSVC AND NOT MINGW ) OR APPLE ) ) endif() MESSAGE( STATUS "HIVE_STATIC_BUILD: ${HIVE_STATIC_BUILD}" ) -SET( HIVE_LINT_LEVEL "OFF" CACHE STRING "Lint level during Hive build (FULL, HIGH, LOW, OFF)" ) +SET( HIVE_LINT "OFF" CACHE STRING "Enable linting with clang-tidy during compilation" ) find_program( CLANG_TIDY_EXE NAMES "clang-tidy" DOC "Path to clain-tidy executable" ) +SET( CLANG_TIDY_IGNORED +"-fuchsia-default-arguments\ +,-hicpp-*\ +,-cert-err60-cpp\ +,-llvm-namespace-comment\ +,-cert-err09-cpp\ +,-cert-err61-cpp\ +,-fuchsia-overloaded-operator\ +,-misc-throw-by-value-catch-by-reference\ +,-misc-unused-parameters\ +,-clang-analyzer-core.uninitialized.Assign\ +,-llvm-include-order\ +,-clang-diagnostic-unused-lambda-capture\ +,-misc-macro-parentheses\ +,-boost-use-to-string\ +,-misc-lambda-function-name\ +,-cert-err58-cpp\ +,-cert-err34-c\ +,-cppcoreguidelines-*\ +,-modernize-*\ +,-clang-diagnostic-#pragma-messages\ +,-google-*\ +,-readability-*" +) + if( NOT CLANG_TIDY_EXE ) message( STATUS "clang-tidy not found" ) elseif( VERSION LESS 3.6 ) messgae( STATUS "clang-tidy found but only supported with CMake version >= 3.6" ) else() message( STATUS "clany-tidy found: ${CLANG_TIDY_EXE}" ) - if( "${HIVE_LINT_LEVEL}" STREQUAL "FULL" ) - message( STATUS "Linting level set to: FULL" ) - set( DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-checks='*'" ) - elseif( "${HIVE_LINT_LEVEL}" STREQUAL "HIGH" ) - message( STATUS "Linting level set to: HIGH" ) - set( DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-checks='boost-use-to-string,clang-analyzer-*,cppcoreguidelines-*,llvm-*,misc-*,performance-*,readability-*'" ) - elseif( "${HIVE_LINT_LEVEL}" STREQUAL "LOW" ) - message( STATUS "Linting level set to: LOW" ) - set( DO_CLANG_TIDY "${CLANG_TIDY_EXE}" "-checks='clang-analyzer-*'" ) + if( HIVE_LINT ) + message( STATUS "Linting enabled" ) + set( DO_CLANG_TIDY ${CLANG_TIDY_EXE};-checks=*,${CLANG_TIDY_IGNORED};--warnings-as-errors=* ) else() unset( CLANG_TIDY_EXE ) - message( STATUS "Linting level set to: OFF" ) + message( STATUS "Linting disabled" ) endif() endif( NOT CLANG_TIDY_EXE ) @@ -178,9 +182,14 @@ IF( WIN32 ) set(BOOST_ALL_DYN_LINK OFF) # force dynamic linking for all libraries ENDIF(WIN32) +cmake_policy(SET CMP0057 NEW) FIND_PACKAGE(Boost 1.58 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) -if( NOT( Boost_VERSION LESS 106900 ) ) +if( NOT Boost_VERSION_MACRO ) # FindBoost in CMake version >=3.15 reports Boost_VERSION in x.y.z format + set( Boost_VERSION_MACRO Boost_VERSION ) +endif() + +if( NOT( Boost_VERSION_MACRO LESS 106900 ) ) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") endif() @@ -221,7 +230,7 @@ if( WIN32 ) SET(TCL_LIBRARY ${TCL_LIBS}) elseif( MINGW ) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fpermissive -msse4.2 -Wa,-mbig-obj") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -msse4.2 -Wa,-mbig-obj") SET(CMAKE_CXX_FLAGS_RELEASE "-O3") # Optimization flag apparently needed to get rid of "File too big" assembler errors when compiling in Debug mode # See: https://stackoverflow.com/questions/14125007/gcc-string-table-overflow-error-during-compilation/14601779#29479701 @@ -242,11 +251,11 @@ else( WIN32 ) # Apple AND Linux if( APPLE ) # Apple Specific Options Here message( STATUS "Configuring Hive on OS X" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -stdlib=libc++ -Wall -Wno-conversion -Wno-deprecated-declarations" ) + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -stdlib=libc++ -Wall -Wno-conversion -Wno-deprecated-declarations" ) else( APPLE ) # Linux Specific Options Here message( STATUS "Configuring Hive on Linux" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -Wall" ) + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wall" ) set( rt_library rt ) set( pthread_library pthread) if ( NOT DEFINED crypto_library ) @@ -286,7 +295,6 @@ endif() # fc/src/compress/miniz.c breaks strict aliasing. The Linux kernel builds with no strict aliasing SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing -Werror -DBOOST_THREAD_DONT_PROVIDE_PROMISE_LAZY" ) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-strict-aliasing -DBOOST_THREAD_DONT_PROVIDE_PROMISE_LAZY" ) -# -Werror # external_plugins needs to be compiled first because libraries/app depends on HIVE_EXTERNAL_PLUGINS being fully populated add_subdirectory( external_plugins ) @@ -309,7 +317,7 @@ set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/install) SET(CPACK_PACKAGE_DIRECTORY "${CMAKE_INSTALL_PREFIX}") set(CPACK_PACKAGE_NAME "hive") -set(CPACK_PACKAGE_VENDOR "Steemit, Inc.") +set(CPACK_PACKAGE_VENDOR "Hive Community") set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}") set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}") set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}") @@ -356,14 +364,11 @@ else() MESSAGE( STATUS "\n\n CONFIGURED FOR NO SUPPORT OF SMT \n\n" ) endif() -if( ENABLE_MIRA ) -MESSAGE( STATUS "\n\n CONFIGURED FOR MIRA \n\n" ) -else() -MESSAGE( STATUS "\n\n CONFIGURED FOR BMIC MMAP \n\n" ) -endif() - if( LOW_MEMORY_NODE ) MESSAGE( STATUS "\n\n CONFIGURED FOR LOW MEMORY NODE \n\n" ) + if( STORE_ACCOUNT_METADATA ) + MESSAGE( STATUS "\n\n BUT STILL STORING ACCOUNT METADATA \n\n" ) + endif() else() MESSAGE( STATUS "\n\n CONFIGURED FOR FULL NODE \n\n" ) endif() diff --git a/Dockerfile b/Dockerfile index 7cf058a487..e8fe85afd6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,7 @@ #Usage: DOCKER_BUILDKIT=1 docker build --no-cache --target=testnet_node_builder . -ARG LOW_MEMORY_NODE=ON -ARG CLEAR_VOTES=ON ARG BUILD_HIVE_TESTNET=OFF -ARG ENABLE_MIRA=OFF +ARG HIVE_LINT=OFF FROM registry.gitlab.syncad.com/hive/hive/hive-baseenv:latest AS builder ENV src_dir="/usr/local/src/hive" @@ -21,7 +19,7 @@ FROM builder AS consensus_node_builder RUN \ cd ${src_dir} && \ - ${src_dir}/ciscripts/build.sh "ON" "ON" "OFF" "OFF" + ${src_dir}/ciscripts/build.sh "OFF" "ON" ################################################################################################### ## CONSENSUS NODE CONFIGURATION ## @@ -48,60 +46,22 @@ EXPOSE 8090 EXPOSE 2001 CMD "${install_base_dir}/consensus/hived.run" -################################################################################################### -## FAT NODE BUILD ## -################################################################################################### - -FROM builder AS fat_node_builder - -RUN \ - cd ${src_dir} && \ - ${src_dir}/ciscripts/build.sh "OFF" "OFF" "OFF" "ON" - -################################################################################################### -## FAT NODE CONFIGURATION ## -################################################################################################### - -FROM builder AS fat_node -ARG TRACKED_ACCOUNT_NAME -ENV TRACKED_ACCOUNT_NAME=${TRACKED_ACCOUNT_NAME} -ARG USE_PUBLIC_BLOCKLOG -ENV USE_PUBLIC_BLOCKLOG=${USE_PUBLIC_BLOCKLOG} - -WORKDIR "${install_base_dir}/fat-node" -# Get all needed files from previous stage, and throw away unneeded rest(like objects) -COPY --from=fat_node_builder ${src_dir}/build/install-root/ ${src_dir}/contrib/hived.run ./ -COPY --from=fat_node_builder ${src_dir}/contrib/config-for-docker.ini datadir/config.ini - -RUN \ - ls -la && \ - chmod +x hived.run - -# rpc service : -EXPOSE 8090 -# p2p service : -EXPOSE 2001 -CMD "${install_base_dir}/fat-node/hived.run" - ################################################################################################### ## GENERAL NODE BUILD ## ################################################################################################### FROM builder AS general_node_builder -ARG LOW_MEMORY_NODE -ARG CLEAR_VOTES + ARG BUILD_HIVE_TESTNET -ARG ENABLE_MIRA +ARG HIVE_LINT -ENV LOW_MEMORY_NODE=${LOW_MEMORY_NODE} -ENV CLEAR_VOTES=${CLEAR_VOTES} ENV BUILD_HIVE_TESTNET=${BUILD_HIVE_TESTNET} -ENV ENABLE_MIRA=${ENABLE_MIRA} +ENV HIVE_LINT=${HIVE_LINT} RUN \ cd ${src_dir} && \ - ${src_dir}/ciscripts/build.sh ${LOW_MEMORY_NODE} ${CLEAR_VOTES} ${BUILD_HIVE_TESTNET} ${ENABLE_MIRA} + ${src_dir}/ciscripts/build.sh ${BUILD_HIVE_TESTNET} ${HIVE_LINT} ################################################################################################### ## GENERAL NODE CONFIGURATION ## @@ -134,19 +94,17 @@ CMD "${install_base_dir}/hive-node/hived.run" FROM builder AS testnet_node_builder -ARG LOW_MEMORY_NODE=OFF -ARG CLEAR_VOTES=OFF -ARG ENABLE_MIRA=OFF +ARG HIVE_LINT=ON -ENV LOW_MEMORY_NODE=${LOW_MEMORY_NODE} -ENV CLEAR_VOTES=${CLEAR_VOTES} ENV BUILD_HIVE_TESTNET="ON" -ENV ENABLE_MIRA=${ENABLE_MIRA} +ENV HIVE_LINT=${HIVE_LINT} RUN \ cd ${src_dir} && \ - ${src_dir}/ciscripts/build.sh ${LOW_MEMORY_NODE} ${CLEAR_VOTES} ${BUILD_HIVE_TESTNET} ${ENABLE_MIRA} && \ apt-get update && \ + apt-get install -y clang && \ + apt-get install -y clang-tidy && \ + ${src_dir}/ciscripts/build.sh ${BUILD_HIVE_TESTNET} ${HIVE_LINT} && \ apt-get install -y screen && \ pip3 install -U secp256k1prp && \ git clone https://gitlab.syncad.com/hive/beem.git && \ diff --git a/Dockerfile.hive-baseenv b/Dockerfile.hive-baseenv index f003f6b010..8621bda686 100644 --- a/Dockerfile.hive-baseenv +++ b/Dockerfile.hive-baseenv @@ -39,6 +39,8 @@ RUN \ libbz2-dev \ liblz4-dev \ libzstd-dev \ + clang \ + clang-tidy \ && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ diff --git a/README.md b/README.md index 4bf74a587b..f533077c77 100644 --- a/README.md +++ b/README.md @@ -54,27 +54,15 @@ We **strongly** recommend using one of our pre-built Docker images or using Dock But if you would still like to build from source, we also have [build instructions](doc/building.md) for Linux (Ubuntu LTS) and macOS. -## Dockerized P2P Node +## Dockerized Consensus Node -To run a p2p node (ca. 2GB of memory is required at the moment): +To run a Hive node (ca. 16GB of memory is required at the moment): docker run \ - -d -p 2001:2001 -p 8090:8090 --name hived-default \ - openhive-network/hive + -d -p 2001:2001 -p 8090:8090 --name hived \ + hiveio/hive - docker logs -f hived-default # follow along - -## Dockerized Full Node - -To run a node with *all* the data (e.g. for supporting a content website) -ca. 14GB of memory, and growing, is required: - - docker run \ - --env USE_WAY_TOO_MUCH_RAM=1 --env USE_FULL_WEB_NODE=1 \ - -d -p 2001:2001 -p 8090:8090 --name hived-full \ - openhive-network/hive - - docker logs -f hived-full + docker logs -f hived # follow along ## CLI Wallet @@ -89,7 +77,7 @@ on how to use lcov to check code test coverage. ## Config File -Run `hived` once to generate a data directory and config file. The default location is `witness_node_data_dir`. Kill `hived`. It won't do anything without seed nodes. If you want to modify the config to your liking, we have two example configs used in the docker images. ( [consensus node](contrib/config-for-docker.ini), [full node](contrib/fullnode.config.ini) ) All options will be present in the default config file and there may be more options needing to be changed from the docker configs (some of the options actually used in images are configured via command line). +Run `hived` once to generate a data directory and config file. The default data directory location is `~/.hived`. Kill `hived`. If you want to modify the config to your liking, we have [example config](contrib/config-for-docker.ini) used in the docker image. All options will be present in the default config file and there may be more options needing to be changed from the docker configs (some of the options actually used in images are configured via command line). ## Seed Nodes @@ -100,39 +88,10 @@ This same file is baked into the docker images and can be overridden by setting `HIVED_SEED_NODES` in the container environment at `docker run` time to a whitespace delimited list of seed nodes (with port). - -## Environment variables - -There are quite a few environment variables that can be set to run hived in different ways: - -* `USE_WAY_TOO_MUCH_RAM` - if set to true, hived starts a 'full node' -* `USE_FULL_WEB_NODE` - if set to true, a default config file will be used that enables a full set of API's and associated plugins. -* `USE_NGINX_FRONTEND` - if set to true, this will enable an NGINX reverse proxy in front of hived that proxies WebSocket requests to hived. This will also enable a custom healthcheck at the path '/health' that lists how many seconds away from current blockchain time your node is. It will return a '200' if it's less than 60 seconds away from being synced. -* `USE_MULTICORE_READONLY` - if set to true, this will enable hived in multiple reader mode to take advantage of multiple cores (if available). Read requests are handled by the read-only nodes and write requests are forwarded back to the single 'writer' node automatically. NGINX load balances all requests to the reader nodes, 4 per available core. This setting is still considered experimental and may have trouble with some API calls until further development is completed. -* `HOME` - set this to the path where you want hived to store it's data files (block log, shared memory, config file, etc). By default `/var/lib/hived` is used and exists inside the docker container. If you want to use a different mount point (like a ramdisk, or a different drive) then you may want to set this variable to map the volume to your docker container. - -## PaaS mode - -Hived now supports a PaaS mode (platform as a service) that currently works with Amazon's Elastic Beanstalk service. It can be launched using the following environment variables: - -* `USE_PAAS` - if set to true, hived will launch in a format that works with AWS EB. Containers will exit upon failure so that they can be relaunched automatically by ECS. This mode assumes `USE_WAY_TOO_MUCH_RAM` and `USE_FULL_WEB_NODE`, they do not need to be also set. -* `S3_BUCKET` - set this to the name of the S3 bucket where you will store shared memory files for hived in Amazon S3. They will be stored compressed in bz2 format with the file name `blockchain-$VERSION-latest.tar.bz2`, where $VERSION is the release number followed by the git short commit hash stored in each docker image at `/etc/hivedversion`. -* `SYNC_TO_S3` - if set to true, the node will function to only generate shared memory files and upload them to the specified S3 bucket. This makes fast deployments and autoscaling for hived possible. - - ## System Requirements [To Be Added] -On Linux use the following Virtual Memory configuration for the initial sync and subsequent replays. It is not needed for normal operation. - -``` -echo 75 | sudo tee /proc/sys/vm/dirty_background_ratio -echo 1000 | sudo tee /proc/sys/vm/dirty_expire_centisecs -echo 80 | sudo tee /proc/sys/vm/dirty_ratio -echo 30000 | sudo tee /proc/sys/vm/dirty_writeback_centisecs -``` - # No Support & No Warranty THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR diff --git a/ciscripts/build.sh b/ciscripts/build.sh index d15665b3aa..9605ffbda7 100755 --- a/ciscripts/build.sh +++ b/ciscripts/build.sh @@ -1,16 +1,12 @@ #!/bin/bash set -e -LOW_MEMORY_NODE=$1 -CLEAR_VOTES=$2 -BUILD_HIVE_TESTNET=$3 -ENABLE_MIRA=$4 +BUILD_HIVE_TESTNET=$1 +HIVE_LINT=${2:OFF} echo "PWD=${PWD}" -echo "LOW_MEMORY_NODE=${LOW_MEMORY_NODE}" -echo "CLEAR_VOTES=${CLEAR_VOTES}" echo "BUILD_HIVE_TESTNET=${BUILD_HIVE_TESTNET}" -echo "ENABLE_MIRA=${ENABLE_MIRA}" +echo "HIVE_LINT=${HIVE_LINT}" BUILD_DIR="${PWD}/build" CMAKE_BUILD_TYPE=Release @@ -22,14 +18,13 @@ cd ${BUILD_DIR} cmake \ -DCMAKE_INSTALL_PREFIX="${BUILD_DIR}/install-root" \ -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DLOW_MEMORY_NODE=${LOW_MEMORY_NODE} \ - -DCLEAR_VOTES=${CLEAR_VOTES} \ -DSKIP_BY_TX_ID=OFF \ -DBUILD_HIVE_TESTNET=${BUILD_HIVE_TESTNET} \ - -DENABLE_MIRA=${ENABLE_MIRA} \ -DHIVE_STATIC_BUILD=ON \ + -DHIVE_LINT=${HIVE_LINT} \ .. make -j$(nproc) +ldd "${BUILD_DIR}/programs/cli_wallet/cli_wallet" # Check HIVE_STATIC_BUILD make install cd .. diff --git a/ciscripts/buildfailure.sh b/ciscripts/buildfailure.sh deleted file mode 100755 index a87c34e6ce..0000000000 --- a/ciscripts/buildfailure.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -curl --silent -XPOST -H "Authorization: token $GITHUB_SECRET" https://api.github.com/repos/steemit/hive/statuses/$(git rev-parse HEAD) -d "{ - \"state\": \"failure\", - \"target_url\": \"${BUILD_URL}\", - \"description\": \"JenkinsCI reports the build has failed!\", - \"context\": \"jenkins-ci-steemit\" -}" -rm -rf $WORKSPACE/* -# make docker cleanup after itself and delete all exited containers -sudo docker rm -v $(docker ps -a -q -f status=exited) || true diff --git a/ciscripts/buildpending.sh b/ciscripts/buildpending.sh deleted file mode 100755 index 94c50aa243..0000000000 --- a/ciscripts/buildpending.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -curl --silent -XPOST -H "Authorization: token $GITHUB_SECRET" https://api.github.com/repos/steemit/hive/statuses/$(git rev-parse HEAD) -d "{ - \"state\": \"pending\", - \"target_url\": \"${BUILD_URL}\", - \"description\": \"The build is now pending in jenkinsci!\", - \"context\": \"jenkins-ci-steemit\" -}" diff --git a/ciscripts/buildscript.sh b/ciscripts/buildscript.sh deleted file mode 100755 index 8befa62d9a..0000000000 --- a/ciscripts/buildscript.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -set -e -export IMAGE_NAME="steemit/hive:$BRANCH_NAME" -if [[ $IMAGE_NAME == "steemit/hive:stable" ]] ; then - IMAGE_NAME="steemit/hive:latest" -fi -sudo docker build --build-arg CI_BUILD=1 --build-arg BUILD_STEP=2 -t=$IMAGE_NAME . -sudo docker login --username=$DOCKER_USER --password=$DOCKER_PASS -sudo docker push $IMAGE_NAME -# make docker cleanup after itself and delete all exited containers -sudo docker rm -v $(docker ps -a -q -f status=exited) || true diff --git a/ciscripts/buildsuccess.sh b/ciscripts/buildsuccess.sh deleted file mode 100755 index 200e0934f4..0000000000 --- a/ciscripts/buildsuccess.sh +++ /dev/null @@ -1,10 +0,0 @@ -#/bin/bash -curl --silent -XPOST -H "Authorization: token $GITHUB_SECRET" https://api.github.com/repos/steemit/hive/statuses/$(git rev-parse HEAD) -d "{ - \"state\": \"success\", - \"target_url\": \"${BUILD_URL}\", - \"description\": \"Jenkins-CI reports build succeeded!!\", - \"context\": \"jenkins-ci-steemit\" -}" -rm -rf $WORKSPACE/* -# make docker cleanup after itself and delete all exited containers -sudo docker rm -v $(docker ps -a -q -f status=exited) || true diff --git a/ciscripts/buildtests.sh b/ciscripts/buildtests.sh deleted file mode 100755 index 99132a3254..0000000000 --- a/ciscripts/buildtests.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -set -e -sudo docker build --build-arg CI_BUILD=1 --build-arg BUILD_STEP=1 -t=steemit/hive:tests . -sudo docker run -v $WORKSPACE:/var/jenkins steemit/hive:tests cp -r /var/cobertura /var/jenkins -# make docker cleanup after itself and delete all exited containers -sudo docker rm -v $(docker ps -a -q -f status=exited) || true \ No newline at end of file diff --git a/ciscripts/compare_for_get_config.bash b/ciscripts/compare_for_get_config.bash new file mode 100755 index 0000000000..9bb7a9819c --- /dev/null +++ b/ciscripts/compare_for_get_config.bash @@ -0,0 +1,27 @@ +#!/bin/bash + +HIVE_SOURCE_DIR="$1" +GET_CONFIG_CPP_PATH="$HIVE_SOURCE_DIR/libraries/protocol/get_config.cpp" +CONFIG_HPP_PATH="$HIVE_SOURCE_DIR/libraries/protocol/include/hive/protocol/config.hpp" + +DIFF_1="/tmp/diff.$RANDOM.$RANDOM.$RANDOM" +DIFF_2="/tmp/diff.$RANDOM.$RANDOM.$RANDOM" + +# get dumps from get_config.cpp +cat $GET_CONFIG_CPP_PATH | grep "result\[" | cut -d '=' -f 2 | tr -d ' ;' | grep -E '^[A-Z]' | sort -u > $DIFF_1; + +# get #define from config.hpp +cat $CONFIG_HPP_PATH | grep -E "^#define" | cut -d ' ' -f 2 | tr -d ' ;' | sort -u > $DIFF_2; + +# if number o lines are equal 0, there is no diffrences +ret_count=$(diff $DIFF_1 $DIFF_2 | wc -l) + +# print missmatches +if [ $ret_count != 0 ] + then + echo "missmatch between $GET_CONFIG_CPP_PATH and $GET_CONFIG_CPP_PATH:" + diff $DIFF_1 $DIFF_2 +fi + +rm $DIFF_1 $DIFF_2 +exit $ret_count \ No newline at end of file diff --git a/ciscripts/run_regressions.sh b/ciscripts/run_regressions.sh index 859730b277..59dcde5976 100755 --- a/ciscripts/run_regressions.sh +++ b/ciscripts/run_regressions.sh @@ -16,6 +16,17 @@ execute_unittest_group() fi } +# $1 ctest test name +execute_exactly_one_test() +{ + local ctest_test_name=$1 + echo "Start ctest test '${ctest_test_name}'" + if ! ctest -R ^${ctest_test_name}$ --output-on-failure -vv + then + exit 1 + fi +} + execute_hive_functional() { echo "Start hive functional tests" @@ -51,8 +62,8 @@ echo " -execute_unittest_group plugin_test -execute_unittest_group chain_test +execute_exactly_one_test all_plugin_tests +execute_exactly_one_test all_chain_tests execute_hive_functional diff --git a/ciscripts/triggerbuild.sh b/ciscripts/triggerbuild.sh deleted file mode 100755 index 9fb8d45801..0000000000 --- a/ciscripts/triggerbuild.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e -/bin/bash $WORKSPACE/ciscripts/buildpending.sh -if /bin/bash $WORKSPACE/ciscripts/buildscript.sh; then - echo BUILD SUCCESS -else - echo BUILD FAILURE - exit 1 -fi diff --git a/ciscripts/triggertests.sh b/ciscripts/triggertests.sh deleted file mode 100755 index cf9478231e..0000000000 --- a/ciscripts/triggertests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e -if /bin/bash $WORKSPACE/ciscripts/buildtests.sh; then - echo BUILD SUCCESS -else - echo BUILD FAILURE - exit 1 -fi \ No newline at end of file diff --git a/contrib/config-for-ahnode.ini b/contrib/config-for-ahnode.ini index 1cd90c5fc2..cd43e4aaff 100644 --- a/contrib/config-for-ahnode.ini +++ b/contrib/config-for-ahnode.ini @@ -28,6 +28,8 @@ plugin = reputation_api plugin = block_api network_broadcast_api rc_api +plugin = state_snapshot + account-history-rocksdb-path = "blockchain/account-history-rocksdb-storage" shared-file-size = 24G @@ -38,9 +40,7 @@ shared-file-scale-rate = 1000 p2p-endpoint = 0.0.0.0:2001 transaction-status-block-depth = 64000 -transaction-status-track-after-block = 47000000 +transaction-status-track-after-block = 54500000 webserver-http-endpoint = 0.0.0.0:8091 webserver-ws-endpoint = 0.0.0.0:8090 - -webserver-thread-pool-size = 256 diff --git a/contrib/config-for-docker.ini b/contrib/config-for-docker.ini index 81bb02d2d4..fc98593611 100644 --- a/contrib/config-for-docker.ini +++ b/contrib/config-for-docker.ini @@ -21,6 +21,8 @@ plugin = transaction_status_api plugin = block_api network_broadcast_api rc_api +plugin = state_snapshot + account-history-rocksdb-path = "blockchain/account-history-rocksdb-storage" shared-file-size = 24G @@ -42,9 +44,7 @@ flush-state-interval = 0 p2p-endpoint = 0.0.0.0:2001 transaction-status-block-depth = 64000 -transaction-status-track-after-block = 47000000 +transaction-status-track-after-block = 54500000 webserver-http-endpoint = 0.0.0.0:8091 webserver-ws-endpoint = 0.0.0.0:8090 - -webserver-thread-pool-size = 32 diff --git a/contrib/hived.run b/contrib/hived.run index 649d54108e..0840abb437 100644 --- a/contrib/hived.run +++ b/contrib/hived.run @@ -28,6 +28,12 @@ if [[ -e ${DATADIR}/blockchain/force_open ]]; then ARGS+=" --force-open" fi +if [[ -e ${DATADIR}/blockchain/load_snapshot ]]; then + echo "We will load latest snapshot" + rm -fv ${DATADIR}/blockchain/load_snapshot + ARGS+=" --load-snapshot latest" +fi + if [[ ! -z "$TRACK_ACCOUNT" ]]; then ARGS+=" --plugin=account_history_rocksdb --plugin=account_history_api" ARGS+=" --account-history-rocksdb-track-account-range=[\"$TRACK_ACCOUNT\",\"$TRACK_ACCOUNT\"]" diff --git a/contrib/pulltestnetscripts.sh b/contrib/pulltestnetscripts.sh deleted file mode 100644 index fba7692b3b..0000000000 --- a/contrib/pulltestnetscripts.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -echo hived-testnet: getting deployment scripts from external source - -wget -qO- $SCRIPTURL/master/$LAUNCHENV/$APP/testnetinit.sh > /usr/local/bin/testnetinit.sh -wget -qO- $SCRIPTURL/master/$LAUNCHENV/$APP/testnet.config.ini > /etc/hived/testnet.config.ini -wget -qO- $SCRIPTURL/master/$LAUNCHENV/$APP/fastgen.config.ini > /etc/hived/fastgen.config.ini -chmod +x /usr/local/bin/testnetinit.sh - -echo hived-testnet: launching testnetinit script - -/usr/local/bin/testnetinit.sh diff --git a/doc/building.md b/doc/building.md index 8d255c316c..eb3a3e36c4 100644 --- a/doc/building.md +++ b/doc/building.md @@ -10,16 +10,6 @@ Specifies whether to build with or without optimization and without or with the symbol table for debugging. Unless you are specifically debugging or running tests, it is recommended to build as release. -### LOW_MEMORY_NODE=[OFF/ON] - -Builds hived to be a consensus-only low memory node. Data and fields not -needed for consensus are not stored in the object database. This option is -recommended for witnesses and seed-nodes. - -### CLEAR_VOTES=[ON/OFF] - -Clears old votes from memory that are no longer required for consensus. - ### BUILD_HIVE_TESTNET=[OFF/ON] Builds hived for use in a private testnet. Also required for building unit tests. @@ -38,9 +28,9 @@ We ship a Dockerfile. This builds both common node type binaries. cd hive docker build -t hiveio/hive . -## Building on Ubuntu 18.04/16.04 +## Building on Ubuntu 18.04/20.04 -For Ubuntu 16.04 users, after installing the right packages with `apt` Hive +For Ubuntu 18.04/20.04 users, after installing the right packages with `apt` Hive will build out of the box without further effort: sudo apt-get update @@ -52,12 +42,17 @@ will build out of the box without further effort: cmake \ g++ \ git \ + zlib1g-dev \ libbz2-dev \ libsnappy-dev \ libssl-dev \ libtool \ make \ pkg-config \ + doxygen \ + libncurses5-dev \ + libreadline-dev \ + perl \ python3 \ python3-jinja2 @@ -77,87 +72,21 @@ will build out of the box without further effort: libboost-test-dev \ libboost-thread-dev - # Optional packages (not required, but will make a nicer experience) - sudo apt-get install -y \ - doxygen \ - libncurses5-dev \ - libreadline-dev \ - perl - git clone https://github.com/openhive-network/hive cd hive - git checkout stable + git checkout master git submodule update --init --recursive - mkdir build - cd build + mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. make -j$(nproc) hived make -j$(nproc) cli_wallet # optional make install # defaults to /usr/local -## Building on Ubuntu 14.04 +If at any time you find this documentation not up to date or unprecise, please take a look at CI/CD scripts. -(It is strongly advised to use Ubuntu 16.04 LTS instead) - -Here are the required packages: - - # Required packages - sudo apt-get install -y \ - autoconf \ - cmake3 \ - g++ \ - git \ - libbz2-dev \ - libsnappy-dev \ - libssl-dev \ - libtool \ - make \ - pkg-config \ - doxygen \ - libncurses5-dev \ - libreadline-dev \ - libbz2-dev \ - python-dev \ - perl \ - python3 \ - python3-jinja2 - -The Boost provided in the Ubuntu 14.04 package manager (Boost 1.55) is too old. -Hive requires Boost 1.58 (as in Ubuntu 16.04) and works with versions up to 1.60 (including). -So building Hive on Ubuntu 14.04 requires downloading and installing a more recent -version of Boost. - -According to [this mailing list -post](http://boost.2283326.n4.nabble.com/1-58-1-bugfix-release-necessary-td4674686.html), -Boost 1.58 is not compatible with gcc 4.8 (the default C++ compiler for -Ubuntu 14.04) when compiling in C++11 mode (which Hive does). -So we will use Boost 1.60. - -Here is how to build and install Boost 1.60 into your user's home directory -(make sure you install all the packages above first): - - export BOOST_ROOT=$HOME/opt/boost_1_60_0 - URL='http://sourceforge.net/projects/boost/files/boost/1.60.0/boost_1_60_0.tar.bz2/download' - wget -c "$URL" -O boost_1_60_0.tar.bz2 - [ $( sha256sum boost_1_60_0.tar.bz2 | cut -d ' ' -f 1 ) == \ - "686affff989ac2488f79a97b9479efb9f2abae035b5ed4d8226de6857933fd3b" ] \ - || ( echo 'Corrupt download' ; exit 1 ) - tar xjf boost_1_60_0.tar.bz2 - cd boost_1_60_0 - ./bootstrap.sh "--prefix=$BOOST_ROOT" - ./b2 install - -Then the instructions are the same as for hive: - - git clone https://github.com/openhive-network/hive - cd hive - git checkout stable - git submodule update --init --recursive - mkdir build && cd build - cmake -DCMAKE_BUILD_TYPE=Release .. - make -j$(nproc) hived - make -j$(nproc) cli_wallet +Known issues: +In Ubuntu 20.04 there's no longer `libboost-signals-dev` available. Removing it shouldn't be a problem because other boost libs depends on `libboost-dev` anyway (which includes signals2). ## Building on macOS X diff --git a/doc/curation-rewards-old-new-algorithm.xlsx b/doc/curation-rewards-old-new-algorithm.xlsx new file mode 100644 index 0000000000..656ba70da3 Binary files /dev/null and b/doc/curation-rewards-old-new-algorithm.xlsx differ diff --git a/doc/devs/sqrt.md b/doc/devs/sqrt.md index f4ceae31b4..818733cb9b 100644 --- a/doc/devs/sqrt.md +++ b/doc/devs/sqrt.md @@ -1,8 +1,15 @@ - # Introduction In this document we derive the approximate integer square root function used by Hive for the curation curve -[here](https://github.com/steemit/hive/issues/1052). +[here](https://github.com/steemit/steem/issues/1052). + +Quoting `mvandeberg` here because original issue content might become unavailable: + +> The curation rewards curve should be changed to a square root function. +> +> We need to decide how to handle curation rewards of posts prior to the hardfork. Because the actual hardfork time is flexible, I propose having two weight fields in the comment_vote_object and every vote on a comment for the week prior to the hardfork tracks both weights. Then, when the hardfork occurs, we can determine when the first comment paid out on the new curve was created and make a clean break at that particular time to optimize the implementation later. +> +> This issue is a WIP while we are discussing the HF19 proposal [here](https://hive.blog/@steemitblog/proposing-steem-equality-0-19-0-as-the-next-fork). # MSB function diff --git a/doc/example_config.ini b/doc/example_config.ini index 2f37f2885d..1c117d488f 100644 --- a/doc/example_config.ini +++ b/doc/example_config.ini @@ -17,6 +17,8 @@ plugin = transaction_status_api plugin = block_api network_broadcast_api rc_api +plugin = state_snapshot + account-history-rocksdb-path = "blockchain/account-history-rocksdb-storage" shared-file-size = 24G @@ -35,7 +37,7 @@ account-history-rocksdb-track-account-range = ["huobi-pro","huobi-pro"] p2p-endpoint = 0.0.0.0:2001 transaction-status-block-depth = 64000 -transaction-status-track-after-block = 47000000 +transaction-status-track-after-block = 54500000 webserver-http-endpoint = 0.0.0.0:8091 webserver-ws-endpoint = 0.0.0.0:8090 diff --git a/doc/exchangequickstart.md b/doc/exchangequickstart.md index da066b166d..881e4889fc 100644 --- a/doc/exchangequickstart.md +++ b/doc/exchangequickstart.md @@ -1,11 +1,11 @@ Exchange Quickstart ------------------- -System Requirements: A dedicated server or virtual machine with a minimum of 20GB of RAM, and at least 350GB of fast **local** SSD storage. Hive is one of the most active blockchains in the world and handles an incredibly large amount of transactions per second, as such, it requires fast storage to run efficiently. +System Requirements: A dedicated server or virtual machine with a minimum of 20GB of RAM, and at least 400GB of fast **local** storage (such as SSD or NVMe). Hive is one of the most active blockchains in the world and handles an incredibly large amount of transactions per second, as such, it requires fast storage to run efficiently. With the right equipment and technical configuration a reindex should take **no longer than 36 hours**. If recommendations are not followed precisely, the reindex can drag on for days or even weeks with significant slowdowns towards the end. -Physically attached SSD will ensure an optimal reindex time. SSD over a NAS or some kind of network storage backed by SSD will often have much higher latency. As an example, AWS EBS is not performant enough. A good recommended instance in AWS is the i3.xlarge, it comes with a physically attached nVME drive (it must be formatted and mounted on instance launch). +Physically attached SSD will ensure an optimal reindex time. SSD over a NAS or some kind of network storage backed by SSD will often have much higher latency. As an example, AWS EBS is not performant enough. A good recommended instance in AWS is the i3.xlarge, it comes with a physically attached NVMe drive (it must be formatted and mounted on instance launch). You can save a lot of time by replaying from a `block_log`. Using the docker method below, we have made it easy to download a `block_log` at launch and replay from it by passing in the `USE_PUBLIC_BLOCKLOG=1` environment variable. To do this, make sure your `blockchain` directory is empty and does not contain a `block_log`. If you are not using docker, you can download a `block_log` from [here](https://gtg.openhive.network/get/blockchain), put it in your Hive data directory, and use the `--replay-blockchain` command line option. Be sure to remove the option if you have to stop/restart hived after already being synced. @@ -13,7 +13,7 @@ We recommend using docker to both build and run Hive for exchanges. Docker is th ### Install docker and git (if not already installed) -On Ubuntu 16.04+: +On Ubuntu 18.04+: ``` apt-get update && apt-get install git docker.io -y ``` diff --git a/doc/mira-tuning.md b/doc/mira-tuning.md deleted file mode 100644 index d31e21ebd9..0000000000 --- a/doc/mira-tuning.md +++ /dev/null @@ -1,132 +0,0 @@ -# Preface - -After MIRAs initial development efforts we released the [Basic MIRA Configuration Guide](https://github.com/openhive-network/hive/blob/master/doc/mira.md) to help bootstrap users attempting to use MIRA enabled `hived`. There is actually much more fine tuning that can be done to improve MIRA's performance. We will break up this process into three phases: - -* Phase 1: Gathering statistics -* Phase 2: Analyzing statistics -* Phase 3: Applying performance recommendations - -# Phase 1: Gathering statistics - -As you may have noticed, within the `database.cfg` file, there is a global option called `statistics`. By default this is set to `false`. This must be set to `true` before proceeding! Here is an example of a `database.cfg` with statistics enabled: - -``` -$ cat ~/.hived/database.cfg -{ - "global": { - "shared_cache": { - "capacity": "5368709120" - }, - "write_buffer_manager": { - "write_buffer_size": "1073741824" - }, - "object_count": 62500, - "statistics": true - }, - "base": { - "optimize_level_style_compaction": true, - "increase_parallelism": true, - "block_based_table_options": { - "block_size": 8192, - "cache_index_and_filter_blocks": true, - "bloom_filter_policy": { - "bits_per_key": 10, - "use_block_based_builder": false - } - } - } -} - -``` - -Once statistics has been enabled, simply perform the action you'd like to optimize. In my example, I will be syncing up the testnet. Start `hived` like you otherwise normally would. Please be aware that enabling statistics causes a drastic performance impact - you won't want to run this in production. By default, statistics are dumped every 10 minutes so you will want to run for a while. The more data you gather, the more accurate the performance tuning suggestions will potentially be. - -# Phase 2: Analyzing statistics - -Luckily, you won't need intimate knowledge of RocksDB in order to analyze the statistics data. The developers working on RocksDB have provided us with a tool that can read the gathered statistics and make performance tuning recommendations. This tool can be found within the `hived` repository at `programs/util/rocksdb_advisor.sh`. From the `program/util` directory run the tool: - -``` -$ sh rocksdb_advisor.sh - -``` - -If all goes well, you should get output for each object specified in the `rocksdb_advisor.sh` file. Here is an example of the possible output: - -``` -Advisor for account_authority... -WARNING(TimeSeriesData) check_and_trigger: float division by zero - -Rule: bloom-not-enabled -TimeSeriesCondition: bloom-not-enabled statistics: ['[]rocksdb.bloom.filter.useful.count', '[]rocksdb.bloom.filter.full.positive.count', '[]rocksdb.bloom.filter.full.true.positive.count'] behavior: evaluate_expression expression: keys[0]+keys[1]+keys[2]==0 aggregation_op: avg trigger: {'ENTITY_PLACEHOLDER': [0.0, 0.0, 0.0]} -Suggestion: inc-bloom-bits-per-key option : bloom_bits action : increase suggested_values : ['2'] -scope: entities: -{'ENTITY_PLACEHOLDER'} -scope: col_fam: -{'boost\\:\\:mpl\\:\\:v_item, 0>', 'boost\\:\\:mpl\\:\\:v_item, 0>', 'boost\\:\\:mpl\\:\\:v_item, 0>', 'default'} -``` - -In reality you will get significantly more output than above. For the sake of simplicity, we will work with one performance suggestion. We can see here the `rocksdb_advisor.sh` provided a suggestion for the `account_authority_object` database. - -> Suggestion: inc-bloom-bits-per-key option : bloom_bits action : increase suggested_values : ['2'] - -Let's move on to applying the advisor's suggestions. - -# Phase 3: Applying performance recommendations - -If you want to apply the same options to all databases, you would just change the `base` setting as this is applied to every database within a MIRA enabled `hived` node. - -You may notice that you will get different recommendations for different objects. In MIRA's implementation, each object is its own RocksDB database. How do we implement different options for different databases? - -## Configuration overlays - -A configuration overlay is a set of options overriding the base configuration to be applied to a specified database. In our default configuration, you may notice that one of the objects is called `base`. These settings are applied to every database unless a *configuration overlay* overrides them. A configuration overlay takes the same options as `base`. As an example, we will override `bits_per_key` for the `account_authority_object`. - -``` -{ - "global": { - "shared_cache": { - "capacity": "5368709120" - }, - "write_buffer_manager": { - "write_buffer_size": "1073741824" - }, - "object_count": 62500, - "statistics": true - }, - "base": { - "optimize_level_style_compaction": true, - "increase_parallelism": true, - "block_based_table_options": { - "block_size": 8192, - "cache_index_and_filter_blocks": true, - "bloom_filter_policy": { - "bits_per_key": 10, - "use_block_based_builder": false - } - } - }, - "account_authority_object": { - "block_based_table_options": { - "block_size": 8192, - "cache_index_and_filter_blocks": true, - "bloom_filter_policy": { - "bits_per_key": 12, - "use_block_based_builder": false - } - } - } -} -``` - -> **Note:** When overriding a configuration value, you must override the complete first level option (such as `block_based_table_options` in the above example). - -Even though we did not specify `optimize_level_style_compaction` and `increase_parallelism` to the `account_authority_object` configuration, they are inherited from `base`. - -## Available options - -Not every RocksDB option is made available to MIRA configurations. It is very possible that the RocksDB tool can recommend changing an option that is unavailable through MIRA. Feel free to add it and create a pull request, especially if it is improving your nodes performance. You can see a complete list of available options in the codebase in [libraries/mira/src/configuration.cpp](https://github.com/openhive-network/hive/blob/master/libraries/mira/src/configuration.cpp). View the recommended options and check the list; I tried to preserve the naming conventions during implementation to make this process easier. - -# Conclusion - -You may need to repeat this process to achieve optimal results. There is no guarantee that you will see performance improvements as this is experimental in nature. When you are benchmarking your configuration or you have completed your performance tuning, remember to set `statistics` to `false`. - diff --git a/doc/mira.md b/doc/mira.md deleted file mode 100644 index 15e6438647..0000000000 --- a/doc/mira.md +++ /dev/null @@ -1,141 +0,0 @@ -# Basic MIRA configuration - -MIRA has many options that allow the user to improve performance. For most use cases, the default options will be sufficient. However, one consideration when configuring MIRA is resource limiting. - -MIRA uses a multi-tiered architecture for retrieving and storing data from its databases. Each blockchain index is considered its own distinct database. At a very high level, the tiers can be thought of as follows: - -| Database Tier | Location | Reading | Writing | -|---------------------|---------------|:--------:|:-------:| -| Object cache | Main memory | ☑ | ☑ | -| Global shared cache | Main memory | ☑ | ☐ | -| Gobal write buffer | Main memory | ☑ | ☑ | -| Tier 0 file based | Disk | ☑ | ☑ | -| Tier 1 file based | Disk | ☑ | ☑ | -| ... | Disk | ☑ | ☑ | - -Below is an example of MIRAs default configuration that is designed for a 16GiB node. - -``` -{ - "global": { - "shared_cache": { - "capacity": "5368709120" - }, - "write_buffer_manager": { - "write_buffer_size": "1073741824" - }, - "object_count": 62500, - "statistics": false - }, - "base": { - "optimize_level_style_compaction": true, - "increase_parallelism": true, - "block_based_table_options": { - "block_size": 8192, - "cache_index_and_filter_blocks": true, - "bloom_filter_policy": { - "bits_per_key": 10, - "use_block_based_builder": false - } - } - } -} -``` - -Lets break down the most important options with regards to limiting main memory usage. - ---- - -## Object cache - -The object cache determines how many database objects MIRA has direct access to. When the application logic accesses the database for reading or writing, it does so through an object within the object cache. Having objects in the object cache circumvents the need to make database accesses to the underlying tiers; this is the most performant layer in the MIRA architecture. Since it is not possible to set the object cache limitations by memory, this layer must be configured according to its heaviest possible usage. The object cache is shared amongst all blockchain index databases. The largest object can be 64KiB, which correlates to the maximum size of one block in the chain. Calculate the heaviest possible usage with the formula `object_count * 64KiB`. Using the default configuration as an example, `62500 * 64 * 1024 = 4096000000` or simply `62500 * 64KiB = 4GiB`. - -``` -... - "write_buffer_size": "1073741824" - }, - "object_count": 62500, <-- Set the object count to limit the object cache usage - "statistics": false - }, - "base": { -... -``` - -> *__Note:__* *The object count is defined as an unsigned integer in the configuration.* - ---- - -## Global shared cache - -The global shared cache is a section of main memory that will contain data blocks used for the retrieval of data. It is recommended that the user set its capacity to about 1/3 of their total memory budget. The global shared cache is shared between all blockchain index databases. Using the default configuration as an example, `5368709120 = 5GiB`. - -``` -{ - "global": { - "shared_cache": { - "capacity": "5368709120" <-- Set the capacity to limit global shared cache usage - }, -... -``` - -> *__Note:__* *The global shared cache capacity is defined as a string in the configuration.* - ---- - -## Global write buffer - -The global write buffer is used for performant writes to the database. It lives in main memory and also contains the latest changes to a particular database object. Not only is it used for writing, but also for reading. It is important to ensure there is enough capacity allocated to keep up with the live chain. The global write buffer is contained *within* the global shared cache. Using the default configuration as an example, `1073741824 = 1GiB`. - -``` -... - "write_buffer_manager": { - "write_buffer_size": "1073741824" <-- Set the write buffer size to limit the write buffer usage - }, -... -``` - -> *__Note:__* *The global write buffer size is defined as a string in the configuration.* - ---- - -## Application memory - -When configuring MIRA it is important to consider the normal memory usage of `hived`. Regardless of the MIRA configuration, `hived` will tend to use roughly 5.5GiB of memory. - ---- - -## Examples - -### Example 1. - -Example 1 is appropriate for a 16GiB node. - -Using the default configuration: - -1. Object count: `62500` -2. Global shared cache: `5368709120` or 5GiB -3. Global write buffer: `1073741824` or 1GiB -4. Approximate application memory usage: `5905580032` or 5.5GiB - -Let us calculate the largest total memory usage of MIRA. `(62500 * 64KiB) + 5GiB + 5.5GiB = ~14.5GiB` - -> *__Note:__* *The global write buffer size is not included in the total memory calculation for MIRA because it is contained within the global shared cache.* - -### Example 2. - -Example 2 is appropriate for a 32GiB node. - -1. Object count: `125000` -2. Global shared cache: `10737418240` or 10GiB -3. Global write buffer: `2147483648` or 2GiB -4. Approximate application memory usage: `5905580032` or 5.5GiB - -Let us calculate the largest total memory usage of MIRA. `(125000 * 64KiB) + 10GiB + 5.5GiB = ~23.5GiB` - -> *__Note:__* *The global write buffer size is not included in the total memory calculation for MIRA because it is contained within the global shared cache.* - ---- - -## Notes - -The optimal configuration for any one specific piece of hardware will vary. This guide is not meant to be definitive, but a baseline to configure MIRA and get up and running. \ No newline at end of file diff --git a/doc/quickstart.md b/doc/quickstart.md deleted file mode 100644 index 467cde8601..0000000000 --- a/doc/quickstart.md +++ /dev/null @@ -1,71 +0,0 @@ -Quickstart ----------- - -### Get current Hive docker -Use docker: -``` -docker run \ - -d -p 2001:2001 -p 8090:8090 --name hived-default \ - --restart unless-stopped hiveio/hive -``` -#### Low memory node? -Above runs low memory node, which is suitable for: -- seed nodes -- witness nodes -- exchanges, etc. -For full api node use: - -``` -docker run \ - --env USE_WAY_TOO_MUCH_RAM=1 --env USE_FULL_WEB_NODE=1 \ - -d -p 2001:2001 -p 8090:8090 --name hived-full \ - --restart unless-stopped \ - hiveio/hive -``` -### Configure for your use case -#### Full API node -You need to use `USE_WAY_TOO_MUCH_RAM=1` and `USE_FULL_WEB_NODE=1` as stated above. -You can Use `contrib/fullnode.config.ini` as a base for your `config.ini` file. - -#### Exchanges -Use low memory node. - -Also make sure that your `config.ini` contains: -``` -enable-plugin = account_history -public-api = database_api login_api -track-account-range = ["yourexchangeid", "yourexchangeid"] -``` -Do not add other APIs or plugins unless you know what you are doing. - -This configuration exists in Docker with the following command - -``` -docker run -d --env TRACK_ACCOUNT="yourexchangeid" \ - --name hived \ - --restart unless-stopped \ - hiveio/hive -``` - -### Resources usage - -Please make sure that you have enough resources available. -Check `shared-file-size =` in your `config.ini` to reflect your needs. -Set it to at least 25% more than current size. - -Provided values are expected to grow significantly over time. - -Blockchain data takes over **16GB** of storage space. - -#### Full node -Shared memory file for full node uses over **65GB** - -#### Exchange node -Shared memory file for exchange node users over **16GB** -(tracked history for single account) - -#### Seed node -Shared memory file for seed node uses over **5.5GB** - -#### Other use cases -Shared memory file size varies, depends on your specific configuration but it is expected to be somewhere between "seed node" and "full node" usage. diff --git a/doc/seednodes.txt b/doc/seednodes.txt index 41701135ef..d8533f4872 100644 --- a/doc/seednodes.txt +++ b/doc/seednodes.txt @@ -5,7 +5,7 @@ hived.splinterlands.com:2001 #aggroed hiveseed-se.privex.io:2001 #privex node.mahdiyari.info:2001 #mahdiyari rpc.ausbit.dev:2001 #ausbitbank -seed.chitty.me:2001 #chitty +api.hive.blog:2001 #blocktrades seed.hivekings.com:2001 #drakos seed.liondani.com:2016 #liondani seed.openhive.network:2001 #gtg diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 0b88c15634..644059cd13 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -4,7 +4,6 @@ add_subdirectory( schema ) add_subdirectory( jsonball ) add_subdirectory( appbase ) add_subdirectory( chainbase ) -add_subdirectory( mira ) add_subdirectory( chain ) add_subdirectory( protocol ) add_subdirectory( net ) diff --git a/libraries/appbase/CMakeLists.txt b/libraries/appbase/CMakeLists.txt index 6cb524b1ae..219ae507c9 100644 --- a/libraries/appbase/CMakeLists.txt +++ b/libraries/appbase/CMakeLists.txt @@ -15,17 +15,18 @@ LIST(APPEND BOOST_COMPONENTS thread unit_test_framework locale) +cmake_policy(SET CMP0057 NEW) FIND_PACKAGE(Boost 1.58 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) set( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" ) if( APPLE ) # Apple Specific Options Here message( STATUS "Configuring AppBase on OS X" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++14 -stdlib=libc++ -Wall -Wno-conversion -Wno-deprecated-declarations" ) + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -stdlib=libc++ -Wall -Wno-conversion -Wno-deprecated-declarations" ) else( APPLE ) # Linux Specific Options Here message( STATUS "Configuring AppBase on Linux" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++14 -Wall" ) + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wall" ) set( rt_library rt ) set( pthread_library pthread) if ( FULL_STATIC_BUILD ) diff --git a/libraries/appbase/application.cpp b/libraries/appbase/application.cpp index 4401759af1..b4efb2d720 100644 --- a/libraries/appbase/application.cpp +++ b/libraries/appbase/application.cpp @@ -1,438 +1,477 @@ -#include - -#include -#include -#include - -#include -#include -#include - -namespace appbase { - -class quoted -{ -public: - explicit quoted(const char* s) - : data(s) - {} - - friend std::ostream& operator<<(std::ostream& stream, const quoted& q) - { - return stream << "\"" << q.data << "\""; - } -private: - const char* data; -}; - -namespace bpo = boost::program_options; -using bpo::options_description; -using bpo::variables_map; -using std::cout; - -io_handler::io_handler( bool _allow_close_when_signal_is_received, final_action_type&& _final_action ) - : allow_close_when_signal_is_received( _allow_close_when_signal_is_received ), final_action( _final_action ) -{ -} - -boost::asio::io_service& io_handler::get_io_service() -{ - return io_serv; -} - -void io_handler::close() -{ - while( lock.test_and_set( std::memory_order_acquire ) ); - - if( !closed ) - { - final_action(); - - close_signal(); - io_serv.stop(); - - closed = true; - } - - lock.clear( std::memory_order_release ); -} - -void io_handler::close_signal() -{ - if( !signals ) - return; - - boost::system::error_code ec; - signals->cancel( ec ); - signals.reset(); - - if( ec.value() != 0 ) - cout<<"Error during cancelling signal: "<< ec.message() << std::endl; -} - -void io_handler::handle_signal( uint32_t _last_signal_code ) -{ - set_interrupt_request( _last_signal_code ); - - if( allow_close_when_signal_is_received ) - close(); -} - -void io_handler::attach_signals() -{ - /** To avoid killing process by broken pipe and continue regular app shutdown. - * Useful for usecase: `hived | tee hived.log` and pressing Ctrl+C - **/ - signal(SIGPIPE, SIG_IGN); - - signals = p_signal_set( new boost::asio::signal_set( io_serv, SIGINT, SIGTERM ) ); - signals->async_wait([ this ](const boost::system::error_code& err, int signal_number ) { - handle_signal( signal_number ); - }); -} - -void io_handler::run() -{ - io_serv.run(); -} - -void io_handler::set_interrupt_request( uint32_t _last_signal_code ) -{ - last_signal_code = _last_signal_code; -} - -bool io_handler::is_interrupt_request() const -{ - return last_signal_code != 0; -} - -class application_impl { - public: - application_impl():_app_options("Application Options"){ - } - const variables_map* _options = nullptr; - options_description _app_options; - options_description _cfg_options; - variables_map _args; - - bfs::path _data_dir; -}; - -application::application() -:my(new application_impl()), main_io_handler( true/*allow_close_when_signal_is_received*/, [ this ](){ shutdown(); } ) -{ -} - -application::~application() { } - -void application::startup() { - - startup_io_handler = io_handler::p_io_handler( new io_handler ( false/*allow_close_when_signal_is_received*/, - [ this ]() - { - _is_interrupt_request = startup_io_handler->is_interrupt_request(); - } - ) ); - startup_io_handler->attach_signals(); - - std::thread startup_thread = std::thread( [&]() - { - startup_io_handler->run(); - }); - - for (const auto& plugin : initialized_plugins) - { - plugin->startup(); - - if( is_interrupt_request() ) - break; - } - - startup_io_handler->close(); - startup_thread.join(); -} - -application& application::instance( bool reset ) { - static application* _app = new application(); - if( reset ) - { - delete _app; - _app = new application(); - } - return *_app; -} -application& app() { return application::instance(); } -application& reset() { return application::instance( true ); } - - -void application::set_program_options() -{ - std::stringstream data_dir_ss; - data_dir_ss << "Directory containing configuration file config.ini. Default location: $HOME/." << app_name << " or CWD/. " << app_name; - - std::stringstream plugins_ss; - for( auto& p : default_plugins ) - { - plugins_ss << p << ' '; - } - - options_description app_cfg_opts( "Application Config Options" ); - options_description app_cli_opts( "Application Command Line Options" ); - app_cfg_opts.add_options() - ("plugin", bpo::value< vector >()->composing()->default_value( default_plugins, plugins_ss.str() ), "Plugin(s) to enable, may be specified multiple times"); - - app_cli_opts.add_options() - ("help,h", "Print this help message and exit.") - ("version,v", "Print version information.") - ("dump-config", "Dump configuration and exit") - ("data-dir,d", bpo::value(), data_dir_ss.str().c_str() ) - ("config,c", bpo::value()->default_value( "config.ini" ), "Configuration file name relative to data-dir"); - - my->_cfg_options.add(app_cfg_opts); - my->_app_options.add(app_cfg_opts); - my->_app_options.add(app_cli_opts); - - for(auto& plug : plugins) { - boost::program_options::options_description plugin_cli_opts("Command Line Options for " + plug.second->get_name()); - boost::program_options::options_description plugin_cfg_opts("Config Options for " + plug.second->get_name()); - plug.second->set_program_options(plugin_cli_opts, plugin_cfg_opts); - if(plugin_cli_opts.options().size()) - my->_app_options.add(plugin_cli_opts); - if(plugin_cfg_opts.options().size()) - { - my->_cfg_options.add(plugin_cfg_opts); - - for(const boost::shared_ptr od : plugin_cfg_opts.options()) - { - // If the config option is not already present as a cli option, add it. - if( plugin_cli_opts.find_nothrow( od->long_name(), false ) == nullptr ) - { - my->_app_options.add( od ); - } - } - } - } -} - -bool application::initialize_impl(int argc, char** argv, vector autostart_plugins) -{ - try - { - set_program_options(); - bpo::store( bpo::parse_command_line( argc, argv, my->_app_options ), my->_args ); - - if( my->_args.count( "help" ) ) { - cout << my->_app_options << "\n"; - return false; - } - - if( my->_args.count( "version" ) ) - { - cout << version_info << "\n"; - return false; - } - - bfs::path data_dir; - if( my->_args.count("data-dir") ) - { - data_dir = my->_args["data-dir"].as(); - if( data_dir.is_relative() ) - data_dir = bfs::current_path() / data_dir; - } - else - { -#ifdef WIN32 - char* parent = getenv( "APPDATA" ); -#else - char* parent = getenv( "HOME" ); -#endif - - if( parent != nullptr ) - { - data_dir = std::string( parent ); - } - else - { - data_dir = bfs::current_path(); - } - - std::stringstream app_dir; - app_dir << '.' << app_name; - - data_dir = data_dir / app_dir.str(); - - #pragma message( "TODO: Remove this check for Hive release 0.20.1+" ) - bfs::path old_dir = bfs::current_path() / "witness_node_data_dir"; - if( bfs::exists( old_dir ) ) - { - std::cerr << "The default data directory is now '" << data_dir.string() << "' instead of '" << old_dir.string() << "'.\n"; - std::cerr << "Please move your data directory to '" << data_dir.string() << "' or specify '--data-dir=" << old_dir.string() << - "' to continue using the current data directory.\n"; - exit(1); - } - } - my->_data_dir = data_dir; - - bfs::path config_file_name = data_dir / "config.ini"; - if( my->_args.count( "config" ) ) { - config_file_name = my->_args["config"].as(); - if( config_file_name.is_relative() ) - config_file_name = data_dir / config_file_name; - } - - if(!bfs::exists(config_file_name)) { - write_default_config(config_file_name); - } - - bpo::store(bpo::parse_config_file< char >( config_file_name.make_preferred().string().c_str(), - my->_cfg_options, true ), my->_args ); - - if(my->_args.count("dump-config") > 0) - { - std::cout << "{\n"; - std::cout << "\t" << quoted("data-dir") << ": " << quoted(my->_data_dir.string().c_str()) << ",\n"; - std::cout << "\t" << quoted("config") << ": " << quoted(config_file_name.string().c_str()) << "\n"; - std::cout << "}\n"; - return false; - } - - if(my->_args.count("plugin") > 0) - { - auto plugins = my->_args.at("plugin").as>(); - for(auto& arg : plugins) - { - vector names; - boost::split(names, arg, boost::is_any_of(" \t,")); - for(const std::string& name : names) - get_plugin(name).initialize(my->_args); - } - } - for (const auto& plugin : autostart_plugins) - if (plugin != nullptr && plugin->get_state() == abstract_plugin::registered) - plugin->initialize(my->_args); - - bpo::notify(my->_args); - - return true; - } - catch (const boost::program_options::error& e) - { - std::cerr << "Error parsing command line: " << e.what() << "\n"; - return false; - } -} - -void application::shutdown() { - - std::cout << "Shutting down...\n"; - - for(auto ritr = running_plugins.rbegin(); - ritr != running_plugins.rend(); ++ritr) { - (*ritr)->shutdown(); - } - for(auto ritr = running_plugins.rbegin(); - ritr != running_plugins.rend(); ++ritr) { - plugins.erase((*ritr)->get_name()); - } - running_plugins.clear(); - initialized_plugins.clear(); - plugins.clear(); -} - -void application::exec() { - - if( !is_interrupt_request() ) - { - main_io_handler.attach_signals(); - - main_io_handler.run(); - } - else - shutdown(); -} - -void application::write_default_config(const bfs::path& cfg_file) { - if(!bfs::exists(cfg_file.parent_path())) - bfs::create_directories(cfg_file.parent_path()); - - std::ofstream out_cfg( bfs::path(cfg_file).make_preferred().string()); - for(const boost::shared_ptr od : my->_cfg_options.options()) - { - if(!od->description().empty()) - out_cfg << "# " << od->description() << "\n"; - boost::any store; - if(!od->semantic()->apply_default(store)) - out_cfg << "# " << od->long_name() << " = \n"; - else - { - auto example = od->format_parameter(); - if( example.empty() ) - { - // This is a boolean switch - out_cfg << od->long_name() << " = " << "false\n"; - } - else if( example.length() <= 7 ) - { - // The string is formatted "arg" - out_cfg << "# " << od->long_name() << " = \n"; - } - else - { - // The string is formatted "arg (=)" - example.erase(0, 6); - example.erase(example.length()-1); - out_cfg << od->long_name() << " = " << example << "\n"; - } - } - out_cfg << "\n"; - } - out_cfg.close(); -} - -abstract_plugin* application::find_plugin( const string& name )const -{ - auto itr = plugins.find( name ); - - if( itr == plugins.end() ) - { - return nullptr; - } - - return itr->second.get(); -} - -abstract_plugin& application::get_plugin(const string& name)const { - auto ptr = find_plugin(name); - if(!ptr) - BOOST_THROW_EXCEPTION(std::runtime_error("unable to find plugin: " + name)); - return *ptr; -} - -bfs::path application::data_dir()const -{ - return my->_data_dir; -} - -void application::add_program_options( const options_description& cli, const options_description& cfg ) -{ - my->_app_options.add( cli ); - my->_app_options.add( cfg ); - my->_cfg_options.add( cfg ); -} - -const variables_map& application::get_args() const -{ - return my->_args; -} - -std::set< std::string > application::get_plugins_names() const -{ - std::set< std::string > res; - - for( auto& plugin : initialized_plugins ) - res.insert( plugin->get_name() ); - - return res; -} - -} /// namespace appbase +#include + +#include +#include +#include + +#include +#include +#include + +namespace appbase { + +class quoted +{ +public: + explicit quoted(const char* s) + : data(s) + {} + + friend std::ostream& operator<<(std::ostream& stream, const quoted& q) + { + return stream << "\"" << q.data << "\""; + } +private: + const char* data; +}; + +namespace bpo = boost::program_options; +using bpo::options_description; +using bpo::variables_map; +using std::cout; + +io_handler::io_handler(application& _app, bool _allow_close_when_signal_is_received, final_action_type&& _final_action ) + : allow_close_when_signal_is_received( _allow_close_when_signal_is_received ), final_action( _final_action ), app(_app) +{ +} + +boost::asio::io_service& io_handler::get_io_service() +{ + return io_serv; +} + +void io_handler::close() +{ + /** Since signals can be processed asynchronously, it would be possible to call this twice + * concurrently while op service is stopping. + */ + while( lock.test_and_set( std::memory_order_acquire ) ) + ; + + if( !closed ) + { + final_action(); + + close_signal(); + io_serv.stop(); + + closed = true; + } + + lock.clear( std::memory_order_release ); +} + +void io_handler::close_signal() +{ + if( !signals ) + return; + + boost::system::error_code ec; + signals->cancel( ec ); + signals.reset(); + + if( ec.value() != 0 ) + cout<<"Error during cancelling signal: "<< ec.message() << std::endl; +} + +void io_handler::handle_signal( uint32_t _last_signal_code ) +{ + last_signal_code = _last_signal_code; + + if(_last_signal_code == SIGINT || _last_signal_code == SIGTERM) + app.generate_interrupt_request(); + + if( allow_close_when_signal_is_received ) + close(); +} + +void io_handler::attach_signals() +{ + /** To avoid killing process by broken pipe and continue regular app shutdown. + * Useful for usecase: `hived | tee hived.log` and pressing Ctrl+C + **/ + signal(SIGPIPE, SIG_IGN); + + signals = p_signal_set( new boost::asio::signal_set( io_serv, SIGINT, SIGTERM ) ); + signals->async_wait([ this ](const boost::system::error_code& err, int signal_number ) { + /// Handle signal only if it was really present (it is possible to get error_code for 'Operation cancelled' together with signal_number == 0) + if(signal_number != 0) + handle_signal( signal_number ); + }); +} + +void io_handler::run() +{ + io_serv.run(); +} + +class application_impl { + public: + application_impl():_app_options("Application Options"){ + } + const variables_map* _options = nullptr; + options_description _app_options; + options_description _cfg_options; + variables_map _args; + + bfs::path _data_dir; +}; + +application::application() +: pre_shutdown_plugins( + []( abstract_plugin* a, abstract_plugin* b ) + { + assert( a && b ); + return a->get_pre_shutdown_order() > b->get_pre_shutdown_order(); + } +), + my(new application_impl()), main_io_handler(*this, true/*allow_close_when_signal_is_received*/, [ this ](){ finish(); } ) +{ +} + +application::~application() { } + +void application::startup() { + + std::cout << "Setting up a startup_io_handler..." << std::endl; + + io_handler startup_io_handler(*this, false/*allow_close_when_signal_is_received*/, []() {}); + startup_io_handler.attach_signals(); + + std::thread startup_thread = std::thread( [&startup_io_handler]() + { + startup_io_handler.run(); + }); + + for (const auto& plugin : initialized_plugins) + { + plugin->startup(); + + if( is_interrupt_request() ) + break; + } + + if( !is_interrupt_request() ) + { + for( const auto& plugin : initialized_plugins ) + { + plugin->finalize_startup(); + } + } + + startup_io_handler.close(); + + startup_thread.join(); +} + +application& application::instance( bool reset ) { + static application* _app = new application(); + if( reset ) + { + delete _app; + _app = new application(); + } + return *_app; +} +application& app() { return application::instance(); } +application& reset() { return application::instance( true ); } + + +void application::set_program_options() +{ + std::stringstream data_dir_ss; + data_dir_ss << "Directory containing configuration file config.ini. Default location: $HOME/." << app_name << " or CWD/. " << app_name; + + std::stringstream plugins_ss; + for( auto& p : default_plugins ) + { + plugins_ss << p << ' '; + } + + options_description app_cfg_opts( "Application Config Options" ); + options_description app_cli_opts( "Application Command Line Options" ); + app_cfg_opts.add_options() + ("plugin", bpo::value< vector >()->composing()->default_value( default_plugins, plugins_ss.str() ), "Plugin(s) to enable, may be specified multiple times"); + + app_cli_opts.add_options() + ("help,h", "Print this help message and exit.") + ("version,v", "Print version information.") + ("dump-config", "Dump configuration and exit") + ("data-dir,d", bpo::value(), data_dir_ss.str().c_str() ) + ("config,c", bpo::value()->default_value( "config.ini" ), "Configuration file name relative to data-dir"); + + my->_cfg_options.add(app_cfg_opts); + my->_app_options.add(app_cfg_opts); + my->_app_options.add(app_cli_opts); + + for(auto& plug : plugins) { + boost::program_options::options_description plugin_cli_opts("Command Line Options for " + plug.second->get_name()); + boost::program_options::options_description plugin_cfg_opts("Config Options for " + plug.second->get_name()); + plug.second->set_program_options(plugin_cli_opts, plugin_cfg_opts); + if(plugin_cli_opts.options().size()) + my->_app_options.add(plugin_cli_opts); + if(plugin_cfg_opts.options().size()) + { + my->_cfg_options.add(plugin_cfg_opts); + + for(const boost::shared_ptr od : plugin_cfg_opts.options()) + { + // If the config option is not already present as a cli option, add it. + if( plugin_cli_opts.find_nothrow( od->long_name(), false ) == nullptr ) + { + my->_app_options.add( od ); + } + } + } + } +} + +bool application::initialize_impl(int argc, char** argv, vector autostart_plugins) +{ + try + { + set_program_options(); + bpo::store( bpo::parse_command_line( argc, argv, my->_app_options ), my->_args ); + + if( my->_args.count( "help" ) ) { + cout << my->_app_options << "\n"; + return false; + } + + if( my->_args.count( "version" ) ) + { + cout << version_info << "\n"; + return false; + } + + bfs::path data_dir; + if( my->_args.count("data-dir") ) + { + data_dir = my->_args["data-dir"].as(); + if( data_dir.is_relative() ) + data_dir = bfs::current_path() / data_dir; + } + else + { +#ifdef WIN32 + char* parent = getenv( "APPDATA" ); +#else + char* parent = getenv( "HOME" ); +#endif + + if( parent != nullptr ) + { + data_dir = std::string( parent ); + } + else + { + data_dir = bfs::current_path(); + } + + std::stringstream app_dir; + app_dir << '.' << app_name; + + data_dir = data_dir / app_dir.str(); + + #pragma message( "TODO: Remove this check for Hive release 0.20.1+" ) + bfs::path old_dir = bfs::current_path() / "witness_node_data_dir"; + if( bfs::exists( old_dir ) ) + { + std::cerr << "The default data directory is now '" << data_dir.string() << "' instead of '" << old_dir.string() << "'.\n"; + std::cerr << "Please move your data directory to '" << data_dir.string() << "' or specify '--data-dir=" << old_dir.string() << + "' to continue using the current data directory.\n"; + exit(1); + } + } + my->_data_dir = data_dir; + + bfs::path config_file_name = data_dir / "config.ini"; + if( my->_args.count( "config" ) ) { + config_file_name = my->_args["config"].as(); + if( config_file_name.is_relative() ) + config_file_name = data_dir / config_file_name; + } + + if(!bfs::exists(config_file_name)) { + write_default_config(config_file_name); + } + + bpo::store(bpo::parse_config_file< char >( config_file_name.make_preferred().string().c_str(), + my->_cfg_options, true ), my->_args ); + + if(my->_args.count("dump-config") > 0) + { + std::cout << "{\n"; + std::cout << "\t" << quoted("data-dir") << ": " << quoted(my->_data_dir.string().c_str()) << ",\n"; + std::cout << "\t" << quoted("config") << ": " << quoted(config_file_name.string().c_str()) << "\n"; + std::cout << "}\n"; + return false; + } + + if(my->_args.count("plugin") > 0) + { + auto plugins = my->_args.at("plugin").as>(); + for(auto& arg : plugins) + { + vector names; + boost::split(names, arg, boost::is_any_of(" \t,")); + for(const std::string& name : names) + get_plugin(name).initialize(my->_args); + } + } + for (const auto& plugin : autostart_plugins) + if (plugin != nullptr && plugin->get_state() == abstract_plugin::registered) + plugin->initialize(my->_args); + + bpo::notify(my->_args); + + return true; + } + catch (const boost::program_options::error& e) + { + std::cerr << "Error parsing command line: " << e.what() << "\n"; + return false; + } +} + +void application::pre_shutdown() +{ + std::cout << "Before shutting down...\n"; + + for( auto& plugin : pre_shutdown_plugins ) + { + plugin->pre_shutdown(); + } + + pre_shutdown_plugins.clear(); +} + +void application::shutdown() { + + std::cout << "Shutting down...\n"; + + for(auto ritr = running_plugins.rbegin(); + ritr != running_plugins.rend(); ++ritr) { + (*ritr)->shutdown(); + } + for(auto ritr = running_plugins.rbegin(); + ritr != running_plugins.rend(); ++ritr) { + plugins.erase((*ritr)->get_name()); + } + running_plugins.clear(); + initialized_plugins.clear(); + plugins.clear(); +} + +void application::finish() +{ + pre_shutdown(); + shutdown(); +} + +void application::exec() +{ + std::cout << ("Entering application main loop...") << std::endl; + + if( !is_interrupt_request() ) + { + main_io_handler.attach_signals(); + + main_io_handler.run(); + } + else + { + std::cout << ("performing shutdown on interrupt request...") << std::endl; + shutdown(); + } + + std::cout << ("Leaving application main loop...") << std::endl; +} + +void application::write_default_config(const bfs::path& cfg_file) +{ + if(!bfs::exists(cfg_file.parent_path())) + bfs::create_directories(cfg_file.parent_path()); + + std::ofstream out_cfg( bfs::path(cfg_file).make_preferred().string()); + for(const boost::shared_ptr od : my->_cfg_options.options()) + { + if(!od->description().empty()) + out_cfg << "# " << od->description() << "\n"; + boost::any store; + if(!od->semantic()->apply_default(store)) + out_cfg << "# " << od->long_name() << " = \n"; + else + { + auto example = od->format_parameter(); + if( example.empty() ) + { + // This is a boolean switch + out_cfg << od->long_name() << " = " << "false\n"; + } + else if( example.length() <= 7 ) + { + // The string is formatted "arg" + out_cfg << "# " << od->long_name() << " = \n"; + } + else + { + // The string is formatted "arg (=)" + example.erase(0, 6); + example.erase(example.length()-1); + out_cfg << od->long_name() << " = " << example << "\n"; + } + } + out_cfg << "\n"; + } + out_cfg.close(); +} + +abstract_plugin* application::find_plugin( const string& name )const +{ + auto itr = plugins.find( name ); + + if( itr == plugins.end() ) + { + return nullptr; + } + + return itr->second.get(); +} + +abstract_plugin& application::get_plugin(const string& name)const +{ + auto ptr = find_plugin(name); + if(!ptr) + BOOST_THROW_EXCEPTION(std::runtime_error("unable to find plugin: " + name)); + return *ptr; +} + +bfs::path application::data_dir()const +{ + return my->_data_dir; +} + +void application::add_program_options( const options_description& cli, const options_description& cfg ) +{ + my->_app_options.add( cli ); + my->_app_options.add( cfg ); + my->_cfg_options.add( cfg ); +} + +const variables_map& application::get_args() const +{ + return my->_args; +} + +std::set< std::string > application::get_plugins_names() const +{ + std::set< std::string > res; + + for( auto& plugin : initialized_plugins ) + res.insert( plugin->get_name() ); + + return res; +} + +} /// namespace appbase diff --git a/libraries/appbase/include/appbase/application.hpp b/libraries/appbase/include/appbase/application.hpp index 8b1c131d4e..080a44c673 100644 --- a/libraries/appbase/include/appbase/application.hpp +++ b/libraries/appbase/include/appbase/application.hpp @@ -1,249 +1,295 @@ -#pragma once -#include -#include -#include -#include -#include - -#include -#include - -#define APPBASE_VERSION_STRING ("appbase 1.0") - -namespace appbase { - - namespace bpo = boost::program_options; - namespace bfs = boost::filesystem; - - class io_handler - { - public: - - using p_io_handler = std::shared_ptr< io_handler >; - using p_signal_set = std::shared_ptr< boost::asio::signal_set >; - using final_action_type = std::function< void() >; - - private: - - std::atomic_flag lock = ATOMIC_FLAG_INIT; - - bool closed = false; - bool allow_close_when_signal_is_received = false; - uint32_t last_signal_code = 0; - - final_action_type final_action; - - p_signal_set signals; - - boost::asio::io_service io_serv; - - void close_signal(); - - void handle_signal( uint32_t _last_signal_code ); - - public: - - io_handler( bool _allow_close_when_signal_is_received, final_action_type&& _final_action ); - - boost::asio::io_service& get_io_service(); - - void close(); - - void attach_signals(); - - void run(); - - void set_interrupt_request( uint32_t _last_signal_code ); - bool is_interrupt_request() const; - }; - - class application - { - public: - ~application(); - - /** - * @brief Looks for the --plugin commandline / config option and calls initialize on those plugins - * - * @tparam Plugin List of plugins to initalize even if not mentioned by configuration. For plugins started by - * configuration settings or dependency resolution, this template has no effect. - * @return true if the application and plugins were initialized, false or exception on error - */ - template< typename... Plugin > - bool initialize( int argc, char** argv ) - { - return initialize_impl( argc, argv, { find_plugin( Plugin::name() )... } ); - } - - void startup(); - void shutdown(); - - /** - * Wait until quit(), SIGINT or SIGTERM and then shutdown - */ - void exec(); - - static application& instance( bool reset = false ); - - template< typename Plugin > - auto& register_plugin() - { - auto existing = find_plugin( Plugin::name() ); - if( existing ) - return *dynamic_cast< Plugin* >( existing ); - - auto plug = std::make_shared< Plugin >(); - plugins[Plugin::name()] = plug; - plug->register_dependencies(); - return *plug; - } - - template< typename Plugin > - Plugin* find_plugin()const - { - Plugin* plugin = dynamic_cast< Plugin* >( find_plugin( Plugin::name() ) ); - - // Do not return plugins that are registered but not at least initialized. - if( plugin != nullptr && plugin->get_state() == abstract_plugin::registered ) - { - return nullptr; - } - - return plugin; - } - - template< typename Plugin > - Plugin& get_plugin()const - { - auto ptr = find_plugin< Plugin >(); - if( ptr == nullptr ) - BOOST_THROW_EXCEPTION( std::runtime_error( "unable to find plugin: " + Plugin::name() ) ); - return *ptr; - } - - bfs::path data_dir()const; - - void add_program_options( const bpo::options_description& cli, const bpo::options_description& cfg ); - const bpo::variables_map& get_args() const; - - void set_version_string( const string& version ) { version_info = version; } - const std::string& get_version_string() const { return version_info; } - void set_app_name( const string& name ) { app_name = name; } - - template< typename... Plugin > - void set_default_plugins() { default_plugins = { Plugin::name()... }; } - - boost::asio::io_service& get_io_service() { return main_io_handler.get_io_service(); } - - void generate_interrupt_request() - { - if( startup_io_handler ) - startup_io_handler->set_interrupt_request( SIGINT ); - } - - bool is_interrupt_request() const - { - return startup_io_handler ? startup_io_handler->is_interrupt_request() : _is_interrupt_request; - } - - std::set< std::string > get_plugins_names() const; - - protected: - template< typename Impl > - friend class plugin; - - bool initialize_impl( int argc, char** argv, vector< abstract_plugin* > autostart_plugins ); - - abstract_plugin* find_plugin( const string& name )const; - abstract_plugin& get_plugin( const string& name )const; - - /** these notifications get called from the plugin when their state changes so that - * the application can call shutdown in the reverse order. - */ - ///@{ - void plugin_initialized( abstract_plugin& plug ) { initialized_plugins.push_back( &plug ); } - void plugin_started( abstract_plugin& plug ) { running_plugins.push_back( &plug ); } - ///@} - - private: - application(); ///< private because application is a singlton that should be accessed via instance() - map< string, std::shared_ptr< abstract_plugin > > plugins; ///< all registered plugins - vector< abstract_plugin* > initialized_plugins; ///< stored in the order they were started running - vector< abstract_plugin* > running_plugins; ///< stored in the order they were started running - std::string version_info; - std::string app_name = "appbase"; - std::vector< std::string > default_plugins; - - void set_program_options(); - void write_default_config( const bfs::path& cfg_file ); - std::unique_ptr< class application_impl > my; - - io_handler main_io_handler; - - //This handler is designed only for startup purposes - io_handler::p_io_handler startup_io_handler; - - bool _is_interrupt_request = false; - }; - - application& app(); - application& reset(); - - template< typename Impl > - class plugin : public abstract_plugin - { - public: - virtual ~plugin() {} - - virtual state get_state() const override { return _state; } - virtual const std::string& get_name()const override final { return Impl::name(); } - - virtual void register_dependencies() - { - this->plugin_for_each_dependency( [&]( abstract_plugin& plug ){} ); - } - - virtual void initialize(const variables_map& options) override final - { - if( _state == registered ) - { - _state = initialized; - this->plugin_for_each_dependency( [&]( abstract_plugin& plug ){ plug.initialize( options ); } ); - this->plugin_initialize( options ); - // std::cout << "Initializing plugin " << Impl::name() << std::endl; - app().plugin_initialized( *this ); - } - if (_state != initialized) - BOOST_THROW_EXCEPTION( std::runtime_error("Initial state was not registered, so final state cannot be initialized.") ); - } - - virtual void startup() override final - { - if( _state == initialized ) - { - _state = started; - this->plugin_for_each_dependency( [&]( abstract_plugin& plug ){ plug.startup(); } ); - this->plugin_startup(); - app().plugin_started( *this ); - } - if (_state != started ) - BOOST_THROW_EXCEPTION( std::runtime_error("Initial state was not initialized, so final state cannot be started.") ); - } - - virtual void shutdown() override final - { - if( _state == started ) - { - _state = stopped; - //ilog( "shutting down plugin ${name}", ("name",name()) ); - this->plugin_shutdown(); - } - } - - protected: - plugin() = default; - - private: - state _state = abstract_plugin::registered; - }; -} +#pragma once +#include +#include +#include +#include +#include + +#include +#include + +#define APPBASE_VERSION_STRING ("appbase 1.0") + +namespace appbase { + + namespace bpo = boost::program_options; + namespace bfs = boost::filesystem; + + class application; + + class io_handler + { + public: + + using p_signal_set = std::shared_ptr< boost::asio::signal_set >; + using final_action_type = std::function< void() >; + + private: + + std::atomic_flag lock = ATOMIC_FLAG_INIT; + + bool closed = false; + bool allow_close_when_signal_is_received = false; + uint32_t last_signal_code = 0; + + final_action_type final_action; + + p_signal_set signals; + + boost::asio::io_service io_serv; + application& app; + + void close_signal(); + + void handle_signal( uint32_t _last_signal_code ); + + public: + io_handler(application& app, bool _allow_close_when_signal_is_received, final_action_type&& _final_action); + + boost::asio::io_service& get_io_service(); + + void close(); + + void attach_signals(); + + void run(); + }; + + class application final + { + public: + application(const application&) = delete; + application& operator=(const application&) = delete; + application(application&&) = delete; + application& operator=(application&&) = delete; + + /** + * @brief Looks for the --plugin commandline / config option and calls initialize on those plugins + * + * @tparam Plugin List of plugins to initalize even if not mentioned by configuration. For plugins started by + * configuration settings or dependency resolution, this template has no effect. + * @return true if the application and plugins were initialized, false or exception on error + */ + template< typename... Plugin > + bool initialize( int argc, char** argv ) + { + return initialize_impl( argc, argv, { find_plugin( Plugin::name() )... } ); + } + + void startup(); + + void pre_shutdown(); + void shutdown(); + + void finish(); + + /** + * Wait until quit(), SIGINT or SIGTERM and then shutdown + */ + void exec(); + + static application& instance( bool reset = false ); + + template< typename Plugin > + auto& register_plugin() + { + auto existing = find_plugin( Plugin::name() ); + if( existing ) + return *dynamic_cast< Plugin* >( existing ); + + auto plug = std::make_shared< Plugin >(); + plugins[Plugin::name()] = plug; + plug->register_dependencies(); + return *plug; + } + + template< typename Plugin > + Plugin* find_plugin()const + { + Plugin* plugin = dynamic_cast< Plugin* >( find_plugin( Plugin::name() ) ); + + // Do not return plugins that are registered but not at least initialized. + if( plugin != nullptr && plugin->get_state() == abstract_plugin::registered ) + { + return nullptr; + } + + return plugin; + } + + template< typename Plugin > + Plugin& get_plugin()const + { + auto ptr = find_plugin< Plugin >(); + if( ptr == nullptr ) + BOOST_THROW_EXCEPTION( std::runtime_error( "unable to find plugin: " + Plugin::name() ) ); + return *ptr; + } + + bfs::path data_dir()const; + + void add_program_options( const bpo::options_description& cli, const bpo::options_description& cfg ); + const bpo::variables_map& get_args() const; + + void set_version_string( const string& version ) { version_info = version; } + const std::string& get_version_string() const { return version_info; } + void set_app_name( const string& name ) { app_name = name; } + + template< typename... Plugin > + void set_default_plugins() { default_plugins = { Plugin::name()... }; } + + boost::asio::io_service& get_io_service() { return main_io_handler.get_io_service(); } + + void generate_interrupt_request() + { + _is_interrupt_request = true; + } + + bool is_interrupt_request() const + { + return _is_interrupt_request; + } + + std::set< std::string > get_plugins_names() const; + + protected: + template< typename Impl > + friend class plugin; + + bool initialize_impl( int argc, char** argv, vector< abstract_plugin* > autostart_plugins ); + + abstract_plugin* find_plugin( const string& name )const; + abstract_plugin& get_plugin( const string& name )const; + + /** these notifications get called from the plugin when their state changes so that + * the application can call shutdown in the reverse order. + */ + ///@{ + void plugin_initialized( abstract_plugin& plug ) { initialized_plugins.push_back( &plug ); } + void plugin_started( abstract_plugin& plug ) + { + running_plugins.push_back( &plug ); + pre_shutdown_plugins.insert( &plug ); + } + ///@} + + private: + application(); ///< private because application is a singleton that should be accessed via instance() + ~application(); + + map< string, std::shared_ptr< abstract_plugin > > plugins; ///< all registered plugins + vector< abstract_plugin* > initialized_plugins; ///< stored in the order they were started running + + using pre_shutdown_cmp = std::function< bool ( abstract_plugin*, abstract_plugin* ) >; + using pre_shutdown_multiset = std::multiset< abstract_plugin*, pre_shutdown_cmp >; + pre_shutdown_multiset pre_shutdown_plugins; ///< stored in the order what is necessary in order to close every plugin in safe way + vector< abstract_plugin* > running_plugins; ///< stored in the order they were started running + std::string version_info; + std::string app_name = "appbase"; + std::vector< std::string > default_plugins; + + void set_program_options(); + void write_default_config( const bfs::path& cfg_file ); + std::unique_ptr< class application_impl > my; + + io_handler main_io_handler; + + std::atomic_bool _is_interrupt_request{false}; + }; + + application& app(); + application& reset(); + + template< typename Impl > + class plugin : public abstract_plugin + { + public: + virtual ~plugin() {} + + virtual pre_shutdown_order get_pre_shutdown_order() const override { return _pre_shutdown_order; } + virtual state get_state() const override { return _state; } + virtual const std::string& get_name()const override final { return Impl::name(); } + + virtual void register_dependencies() + { + this->plugin_for_each_dependency( [&]( abstract_plugin& plug ){} ); + } + + virtual void initialize(const variables_map& options) override final + { + if( _state == registered ) + { + _state = initialized; + this->plugin_for_each_dependency( [&]( abstract_plugin& plug ){ plug.initialize( options ); } ); + this->plugin_initialize( options ); + // std::cout << "Initializing plugin " << Impl::name() << std::endl; + app().plugin_initialized( *this ); + } + if (_state != initialized) + BOOST_THROW_EXCEPTION( std::runtime_error("Initial state was not registered, so final state cannot be initialized.") ); + } + + virtual void startup() override final + { + if( _state == initialized ) + { + _state = started; + this->plugin_for_each_dependency( [&]( abstract_plugin& plug ){ plug.startup(); } ); + this->plugin_startup(); + app().plugin_started( *this ); + } + if (_state != started ) + BOOST_THROW_EXCEPTION( std::runtime_error("Initial state was not initialized, so final state cannot be started.") ); + } + + virtual void finalize_startup() override final + { + this->plugin_finalize_startup(); + } + + virtual void plugin_finalize_startup() override + { + /* + By default most plugins don't need any post-actions during startup. + Problem is with JSON plugin, that has API plugins attached to itself. + Linking plugins into JSON plugin has to be done after startup actions. + */ + } + + virtual void plugin_pre_shutdown() override + { + /* + By default most plugins don't need any pre-actions during shutdown. + A problem appears when P2P plugin receives and sends data into dependent plugins. + In this case is necessary to close P2P plugin as soon as possible. + */ + } + + virtual void pre_shutdown() override final + { + if( _state == started ) + { + this->plugin_pre_shutdown(); + } + } + + virtual void shutdown() override final + { + if( _state == started ) + { + _state = stopped; + //ilog( "shutting down plugin ${name}", ("name",name()) ); + this->plugin_shutdown(); + } + } + + protected: + plugin() = default; + + virtual void set_pre_shutdown_order( pre_shutdown_order val ) { _pre_shutdown_order = val; } + + private: + pre_shutdown_order _pre_shutdown_order = abstract_plugin::basic_order; + state _state = abstract_plugin::registered; + }; +} diff --git a/libraries/appbase/include/appbase/plugin.hpp b/libraries/appbase/include/appbase/plugin.hpp index 6c9f4734d4..b642bcfb87 100644 --- a/libraries/appbase/include/appbase/plugin.hpp +++ b/libraries/appbase/include/appbase/plugin.hpp @@ -35,15 +35,22 @@ namespace appbase { stopped ///< the plugin is no longer running }; + enum pre_shutdown_order { + basic_order = 0, ///most plugins don't need to be prepared before another plugins, therefore it doesn't matter when they will be closed + p2p_order = 1 ///p2p plugin has to reject/break all connections at the start + }; + virtual ~abstract_plugin(){} + virtual pre_shutdown_order get_pre_shutdown_order()const = 0; virtual state get_state()const = 0; virtual const std::string& get_name()const = 0; virtual void set_program_options( options_description& cli, options_description& cfg ) = 0; virtual void initialize(const variables_map& options) = 0; virtual void startup() = 0; + virtual void finalize_startup() = 0; + virtual void pre_shutdown() = 0; virtual void shutdown() = 0; - protected: typedef std::function plugin_processor; @@ -57,15 +64,28 @@ namespace appbase { It is a part of initialization process triggerred by main application. */ virtual void plugin_initialize( const variables_map& options ) = 0; + /** Abstract method to be reimplemented in final plugin implementation. It is a part of startup process triggerred by main application. */ virtual void plugin_startup() = 0; + + /** Abstract method to be reimplemented in final plugin implementation. + It is a part of final stage of startup process triggerred by main application. + */ + virtual void plugin_finalize_startup() = 0; + + /** Abstract method to be reimplemented in final plugin implementation. + It is a part of shutdown process triggerred by main application. + */ + virtual void plugin_pre_shutdown() = 0; + /** Abstract method to be reimplemented in final plugin implementation. It is a part of shutdown process triggerred by main application. */ virtual void plugin_shutdown() = 0; + virtual void set_pre_shutdown_order( pre_shutdown_order val ) = 0; }; template diff --git a/libraries/appbase/include/appbase/shutdown_mgr.hpp b/libraries/appbase/include/appbase/shutdown_mgr.hpp new file mode 100644 index 0000000000..d6f4131526 --- /dev/null +++ b/libraries/appbase/include/appbase/shutdown_mgr.hpp @@ -0,0 +1,162 @@ +#pragma once + +#include + +#include +#include +#include + +namespace hive { + + struct shutdown_state + { + using ptr_shutdown_state = std::shared_ptr< shutdown_state >; + + std::promise promise; + std::shared_future future; + std::atomic_uint activity; + + }; + + class shutdown_mgr + { + private: + + std::string name; + + std::atomic_bool running; + + std::vector< shutdown_state::ptr_shutdown_state > states; + + const char* fStatus(std::future_status s) + { + switch(s) + { + case std::future_status::ready: + return "ready"; + case std::future_status::deferred: + return "deferred"; + case std::future_status::timeout: + return "timeout"; + default: + return "unknown"; + } + } + + void wait( const shutdown_state& state ) + { + FC_ASSERT( !get_running().load(), "Lack of shutdown" ); + + std::future_status res; + uint32_t cnt = 0; + uint32_t time_maximum = 300;//30 seconds + + do + { + if( state.activity.load() != 0 ) + { + res = state.future.wait_for( std::chrono::milliseconds(100) ); + if( res != std::future_status::ready ) + { + ilog("finishing: ${s}, future status: ${fs}", ("s", fStatus( res ) )("fs", std::to_string( state.future.valid() ) ) ); + } + FC_ASSERT( ++cnt <= time_maximum, "Closing the ${name} is terminated", ( "name", name ) ); + } + else + { + res = std::future_status::ready; + } + } + while( res != std::future_status::ready ); + } + + public: + + shutdown_mgr( std::string _name, size_t _nr_actions ) + : name( _name ), running( true ) + { + for( size_t i = 0; i < _nr_actions; ++i ) + { + shutdown_state::ptr_shutdown_state _state( new shutdown_state() ); + _state->future = std::shared_future( _state->promise.get_future() ); + _state->activity.store( 0 ); + + states.emplace_back( _state ); + } + } + + void prepare_shutdown() + { + running.store( false ); + } + + const std::atomic_bool& get_running() const + { + return running; + } + + shutdown_state& get_state( size_t idx ) + { + FC_ASSERT( idx < states.size(), "Incorrect index - lack of correct state" ); + + shutdown_state* _state = states[idx].get(); + FC_ASSERT( _state, "State has NULL value" ); + + return *_state; + } + + void wait() + { + if( get_running().load() ) + return; + + for( auto& state : states ) + { + shutdown_state* _state = state.get(); + FC_ASSERT( _state, "State has NULL value" ); + wait( *_state ); + } + } + }; + + class action_catcher + { + private: + + const std::atomic_bool& running; + shutdown_state& state; + + public: + + action_catcher( const std::atomic_bool& _running, shutdown_state& _state ): + running( _running ), state( _state ) + { + state.activity.store( state.activity.load() + 1 ); + } + + ~action_catcher() + { + state.activity.store( state.activity.load() - 1 ); + + if( running.load() == false && state.future.valid() == false ) + { + ilog("Sending notification to shutdown barrier."); + + try + { + state.promise.set_value(); + } + catch( const std::future_error& e ) + { + ilog("action_catcher: future error exception. ( Code: ${c} )( Message: ${m} )", ( "c", e.code().value() )( "m", e.what() ) ); + } + catch(...) + { + ilog("action_catcher: unknown error exception." ); + } + } + } + + }; + +} diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index 080e18e2b7..cf760fcda6 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -37,7 +37,7 @@ add_library( hive_chain ${HEADERS} ) -target_link_libraries( hive_chain hive_jsonball hive_protocol fc chainbase hive_schema appbase mira +target_link_libraries( hive_chain hive_jsonball hive_protocol fc chainbase hive_schema appbase ${PATCH_MERGE_LIB} ) target_include_directories( hive_chain PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" diff --git a/libraries/chain/block_log.cpp b/libraries/chain/block_log.cpp index 3badf07540..0704f09cbd 100644 --- a/libraries/chain/block_log.cpp +++ b/libraries/chain/block_log.cpp @@ -152,10 +152,10 @@ namespace hive { namespace chain { my->block_file = file; my->index_file = fc::path( file.generic_string() + ".index" ); - my->block_log_fd = ::open(my->block_file.generic_string().c_str(), O_RDWR | O_APPEND | O_CREAT, 0644); + my->block_log_fd = ::open(my->block_file.generic_string().c_str(), O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC, 0644); if (my->block_log_fd == -1) FC_THROW("Error opening block log file ${filename}: ${error}", ("filename", my->block_file)("error", strerror(errno))); - my->block_index_fd = ::open(my->index_file.generic_string().c_str(), O_RDWR | O_APPEND | O_CREAT, 0644); + my->block_index_fd = ::open(my->index_file.generic_string().c_str(), O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC, 0644); if (my->block_index_fd == -1) FC_THROW("Error opening block index file ${filename}: ${error}", ("filename", my->index_file)("error", strerror(errno))); my->block_log_size = get_file_size(my->block_log_fd); @@ -462,7 +462,7 @@ namespace hive { namespace chain { //create and size the new temporary index file (block_log.index.new) fc::path new_index_file(my->index_file.generic_string() + ".new"); const size_t block_index_size = block_num * sizeof(uint64_t); - int new_index_fd = ::open(new_index_file.generic_string().c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644); + int new_index_fd = ::open(new_index_file.generic_string().c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644); if (new_index_fd == -1) FC_THROW("Error opening temporary new index file ${filename}: ${error}", ("filename", new_index_file.generic_string())("error", strerror(errno))); if (ftruncate(new_index_fd, block_index_size) == -1) @@ -572,7 +572,7 @@ namespace hive { namespace chain { #endif //NOT USE_BACKWARD_INDEX ilog("opening new block index"); - my->block_index_fd = ::open(my->index_file.generic_string().c_str(), O_RDWR | O_APPEND | O_CREAT, 0644); + my->block_index_fd = ::open(my->index_file.generic_string().c_str(), O_RDWR | O_APPEND | O_CREAT | O_CLOEXEC, 0644); if (my->block_index_fd == -1) FC_THROW("Error opening block index file ${filename}: ${error}", ("filename", my->index_file)("error", strerror(errno))); //report size of new index file and verify it is the right size for the blocks in block log diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 29d45e6fb3..39994177f8 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -44,8 +44,6 @@ #include -#include - #include #include @@ -55,36 +53,24 @@ #include -long hf24_time() +long next_hf_time() { - long hf24Time = + // current "next hardfork" is HF25 + long hfTime = #ifdef IS_TEST_NET - 1588334400; // Friday, 1 May 2020 12:00:00 GMT + 1622808000; // Friday, 4 June 2021 12:00:00 #else - 1601992800; // Tuesday, 06-Oct-2020 14:00:00 UTC - + 1625061600; // Wednesday, 30 June 2021 14:00:00 #endif /// IS_TEST_NET - const char* value = getenv("HIVE_HF24_TIME"); - if(value != nullptr) - { - hf24Time = atol(value); - ilog("HIVE_HF24_TIME has been specified through environment variable as ${v}, long value: ${l}", ("v", value)("l", hf24Time)); - } - return hf24Time; -} - -long hf23_time() -{ - long hf23Time = 1584712800; // Friday, 20 March 2020 14:00:00 GMT - const char* value = getenv("HIVE_HF23_TIME"); + const char* value = getenv("HIVE_HF25_TIME"); if(value != nullptr) { - hf23Time = atol(value); - ilog("HIVE_HF23_TIME has been specified through environment variable as ${v}, long value: ${l}", ("v", value)("l", hf23Time)); + hfTime = atol(value); + ilog("HIVE_HF25_TIME has been specified through environment variable as ${v}, long value: ${l}", ("v", value)("l", hfTime)); } - return hf23Time; + return hfTime; } namespace hive { namespace chain { @@ -117,8 +103,6 @@ FC_REFLECT( hive::chain::db_schema, (types)(object_types)(operation_type)(custom namespace hive { namespace chain { -using boost::container::flat_set; - struct reward_fund_context { uint128_t recent_claims = 0; @@ -156,13 +140,14 @@ void database::open( const open_args& args ) helpers::environment_extension_resources environment_extension( appbase::app().get_version_string(), - std::move( appbase::app().get_plugins_names() ), + appbase::app().get_plugins_names(), []( const std::string& message ){ wlog( message.c_str() ); } ); - chainbase::database::open( args.shared_mem_dir, args.chainbase_flags, args.shared_file_size, args.database_cfg, &environment_extension ); + chainbase::database::open( args.shared_mem_dir, args.chainbase_flags, args.shared_file_size, args.database_cfg, &environment_extension, args.force_replay ); initialize_indexes(); initialize_evaluators(); + initialize_irreversible_storage(); if( !find< dynamic_global_property_object >() ) with_write_lock( [&]() @@ -177,12 +162,25 @@ void database::open( const open_args& args ) _block_log.open( args.data_dir / "block_log" ); }); + auto hb = head_block_num(); + auto last_irreversible_block = get_last_irreversible_block_num(); + + FC_ASSERT(hb >= last_irreversible_block); + + ilog("Opened a blockchain database holding a state specific to head block: ${hb} and last irreversible block: ${lb}", ("hb", hb)("lb", last_irreversible_block)); + // Rewind all undo state. This should return us to the state at the last irreversible block. with_write_lock( [&]() { -#ifndef ENABLE_MIRA undo_all(); -#endif + + auto new_hb = head_block_num(); + + FC_ASSERT(new_hb >= last_irreversible_block); + + FC_ASSERT(this->get_last_irreversible_block_num() == last_irreversible_block, "Undo operation should not touch irreversible block value"); + + ilog("Blockchain state database is AT IRREVERSIBLE state specific to head block: ${hb} and LIB: ${lb}", ("hb", head_block_num())("lb", this->get_last_irreversible_block_num())); if( args.chainbase_flags & chainbase::skip_env_check ) { @@ -211,6 +209,9 @@ void database::open( const open_args& args ) init_hardforks(); // Writes to local state, but reads from db }); +#ifdef IS_TEST_NET + /// Leave the chain-id passed to cmdline option. +#else with_read_lock( [&]() { const auto& hardforks = get_hardfork_property_object(); @@ -220,7 +221,8 @@ void database::open( const open_args& args ) set_chain_id(HIVE_CHAIN_ID); } }); - +#endif /// IS_TEST_NET + if (args.benchmark.first) { args.benchmark.second(0, get_abstract_index_cntr()); @@ -230,7 +232,6 @@ void database::open( const open_args& args ) _shared_file_full_threshold = args.shared_file_full_threshold; _shared_file_scale_rate = args.shared_file_scale_rate; - _sps_remove_threshold = args.sps_remove_threshold; auto account = find< account_object, by_name >( "nijeah" ); if( account != nullptr && account->to_withdraw < 0 ) @@ -247,35 +248,6 @@ void database::open( const open_args& args ) FC_CAPTURE_LOG_AND_RETHROW( (args.data_dir)(args.shared_mem_dir)(args.shared_file_size) ) } -#ifdef ENABLE_MIRA -void reindex_set_index_helper( database& db, mira::index_type type, const boost::filesystem::path& p, const boost::any& cfg, std::vector< std::string > indices ) -{ - index_delegate_map delegates; - - if ( indices.size() > 0 ) - { - for ( auto& index_name : indices ) - { - if ( db.has_index_delegate( index_name ) ) - delegates[ index_name ] = db.get_index_delegate( index_name ); - else - wlog( "Encountered an unknown index name '${name}'.", ("name", index_name) ); - } - } - else - { - delegates = db.index_delegates(); - } - - std::string type_str = type == mira::index_type::mira ? "mira" : "bmic"; - for ( auto const& delegate : delegates ) - { - ilog( "Converting index '${name}' to ${type} type.", ("name", delegate.first)("type", type_str) ); - delegate.second.set_index_type( db, type, p, cfg ); - } -} -#endif - uint32_t database::reindex_internal( const open_args& args, signed_block& block ) { uint64_t skip_flags = @@ -298,33 +270,17 @@ uint32_t database::reindex_internal( const open_args& args, signed_block& block args.benchmark.second( 0, get_abstract_index_cntr() ); } + bool rat = fc::enable_record_assert_trip; + bool as = fc::enable_assert_stacktrace; + fc::enable_record_assert_trip = true; //enable detailed backtrace from FC_ASSERT (that should not ever be triggered during replay) + fc::enable_assert_stacktrace = true; + while( !appbase::app().is_interrupt_request() && block.block_num() != last_block_num ) { uint32_t cur_block_num = block.block_num(); - if( cur_block_num % 100000 == 0 ) - { - std::cerr << " " << double( cur_block_num ) * 100 / last_block_num << "% " << cur_block_num << " of " << last_block_num << " (" << -#ifdef ENABLE_MIRA - get_cache_size() << " objects cached using " << (get_cache_usage() >> 20) << "M" -#else - (get_free_memory() >> 20) << "M free" -#endif - << ")\n"; - //rocksdb::SetPerfLevel(rocksdb::kEnableCount); - //rocksdb::get_perf_context()->Reset(); - } apply_block( block, skip_flags ); - if( cur_block_num % 100000 == 0 ) - { - //std::cout << rocksdb::get_perf_context()->ToString() << std::endl; - if( cur_block_num % 1000000 == 0 ) - { - dump_lb_call_counts(); - } - } - if( (args.benchmark.first > 0) && (cur_block_num % args.benchmark.first == 0) ) args.benchmark.second( cur_block_num, get_abstract_index_cntr() ); @@ -337,6 +293,9 @@ uint32_t database::reindex_internal( const open_args& args, signed_block& block } } + fc::enable_record_assert_trip = rat; //restore flag + fc::enable_assert_stacktrace = as; + if( appbase::app().is_interrupt_request() ) { ilog("Replaying is interrupted on user request. Last applied: ( block number: ${n} )( trx: ${trx} )", ( "n", block.block_num() )( "trx", block.id() ) ); @@ -380,12 +339,6 @@ uint32_t database::reindex( const open_args& args ) { ilog( "Reindexing Blockchain" ); - if( args.force_replay ) - wipe( args.data_dir, args.shared_mem_dir, false ); - else - close(); - - open( args ); if( appbase::app().is_interrupt_request() ) return 0; @@ -396,14 +349,6 @@ uint32_t database::reindex( const open_args& args ) HIVE_TRY_NOTIFY(_pre_reindex_signal, note); -#ifdef ENABLE_MIRA - if( args.replay_in_memory ) - { - ilog( "Configuring replay to use memory..." ); - reindex_set_index_helper( *this, mira::index_type::bmic, args.shared_mem_dir, args.database_cfg, args.replay_memory_indices ); - } -#endif - _fork_db.reset(); // override effect of _fork_db.start_block() call in open() auto start_time = fc::time_point::now(); @@ -458,14 +403,6 @@ uint32_t database::reindex( const open_args& args ) if ( _block_log.head()->block_num() ) _fork_db.start_block( *_block_log.head() ); -#ifdef ENABLE_MIRA - if ( args.replay_in_memory ) - { - ilog( "Migrating state to disk..." ); - reindex_set_index_helper( *this, mira::index_type::mira, args.shared_mem_dir, args.database_cfg, args.replay_memory_indices ); - } -#endif - auto end_time = fc::time_point::now(); ilog("Done reindexing, elapsed time: ${elapsed_time} sec", ("elapsed_time", double((end_time - start_time).count()) / 1000000.0)); @@ -480,7 +417,8 @@ uint32_t database::reindex( const open_args& args ) void database::wipe( const fc::path& data_dir, const fc::path& shared_mem_dir, bool include_blocks) { - close(); + if( get_is_open() ) + close(); chainbase::database::wipe( shared_mem_dir ); if( include_blocks ) { @@ -493,6 +431,9 @@ void database::close(bool rewind) { try { + if(get_is_open() == false) + wlog("database::close method is MISUSED since it is NOT opened atm..."); + ilog( "Closing database" ); // Since pop_block() will move tx's in the popped blocks into pending, @@ -500,11 +441,12 @@ void database::close(bool rewind) // DB state (issue #336). clear_pending(); -#ifdef ENABLE_MIRA - undo_all(); -#endif - chainbase::database::flush(); + + auto lib = this->get_last_irreversible_block_num(); + + ilog("Database flushed at last irreversible block: ${b}", ("b", lib)); + chainbase::database::close(); _block_log.close(); @@ -647,8 +589,8 @@ std::vector database::fetch_block_range_unlocked( const uint32_t s if (!result.empty()) idump((result.front().block_num())(result.back().block_num())); result.reserve(result.size() + fork_items.size()); - for (const fork_item& item : fork_items) - result.push_back(std::move(item.data)); + for (fork_item& item : fork_items) + result.emplace_back(std::move(item.data)); return result; } FC_LOG_AND_RETHROW() } @@ -686,6 +628,24 @@ chain_id_type database::get_chain_id() const return hive_chain_id; } +chain_id_type database::get_old_chain_id() const +{ +#ifdef IS_TEST_NET + return hive_chain_id; /// In testnet always use the chain-id passed as hived option +#else + return STEEM_CHAIN_ID; +#endif /// IS_TEST_NET +} + +chain_id_type database::get_new_chain_id() const +{ +#ifdef IS_TEST_NET + return hive_chain_id; /// In testnet always use the chain-id passed as hived option +#else + return HIVE_CHAIN_ID; +#endif /// IS_TEST_NET +} + void database::set_chain_id( const chain_id_type& chain_id ) { hive_chain_id = chain_id; @@ -693,7 +653,7 @@ void database::set_chain_id( const chain_id_type& chain_id ) idump( (hive_chain_id) ); } -void database::foreach_block(std::function processor) const +void database::foreach_block(const std::function& processor) const { if(!_block_log.head()) return; @@ -822,7 +782,8 @@ const comment_object* database::find_comment( const account_name_type& author, c return find_comment( acc->get_id(), permlink ); } -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR + const comment_object& database::get_comment( const account_id_type& author, const string& permlink )const { try { return get< comment_object, by_permlink >( comment_object::compute_author_and_permlink_hash( author, permlink ) ); @@ -844,6 +805,7 @@ const comment_object* database::find_comment( const account_name_type& author, c if(acc == nullptr) return nullptr; return find_comment( acc->get_id(), permlink ); } + #endif const escrow_object& database::get_escrow( const account_name_type& name, uint32_t escrow_id )const @@ -1089,6 +1051,7 @@ void database::_maybe_warn_multiple_production( uint32_t height )const if( blocks.size() > 1 ) { vector< std::pair< account_name_type, fc::time_point_sec > > witness_time_pairs; + witness_time_pairs.reserve( blocks.size() ); for( const auto& b : blocks ) { witness_time_pairs.push_back( std::make_pair( b->data.witness, b->data.timestamp ) ); @@ -1428,6 +1391,11 @@ void database::notify_load_snapshot_data_supplement(const load_snapshot_suppleme HIVE_TRY_NOTIFY(_load_snapshot_supplement_signal, note) } +void database::notify_comment_reward(const comment_reward_notification& note) +{ + HIVE_TRY_NOTIFY(_comment_reward_signal, note) +} + account_name_type database::get_scheduled_witness( uint32_t slot_num )const { const dynamic_global_property_object& dpo = get_dynamic_global_properties(); @@ -1577,6 +1545,7 @@ asset database::adjust_account_vesting_balance(const account_object& to_account, else adjust_balance( to_account, new_vesting ); // Update global vesting pool numbers. + const auto& smt = get< smt_token_object, by_symbol >( liquid.symbol ); modify( smt, [&]( smt_token_object& smt_object ) { if( to_reward_balance ) @@ -1647,7 +1616,7 @@ asset database::adjust_account_vesting_balance(const account_object& to_account, // we modify the database. // This allows us to implement virtual op pre-notifications in the Before function. template< typename Before > -asset create_vesting2( database& db, const account_object& to_account, asset liquid, bool to_reward_balance, Before&& before_vesting_callback ) +asset create_vesting2( database& db, const account_object& to_account, const asset& liquid, bool to_reward_balance, Before&& before_vesting_callback ) { try { @@ -1666,7 +1635,7 @@ asset create_vesting2( database& db, const account_object& to_account, asset liq * @param to_account - the account to receive the new vesting shares * @param liquid - HIVE or liquid SMT to be converted to vesting shares */ -asset database::create_vesting( const account_object& to_account, asset liquid, bool to_reward_balance ) +asset database::create_vesting( const account_object& to_account, const asset& liquid, bool to_reward_balance ) { return create_vesting2( *this, to_account, liquid, to_reward_balance, []( asset vests_created ) {} ); } @@ -1699,13 +1668,13 @@ void database::adjust_proxied_witness_votes( const account_object& a, const std::array< share_type, HIVE_MAX_PROXY_RECURSION_DEPTH+1 >& delta, int depth ) { - if( a.proxy != HIVE_PROXY_TO_SELF_ACCOUNT ) + if( a.has_proxy() ) { /// nested proxies are not supported, vote will not propagate if( depth >= HIVE_MAX_PROXY_RECURSION_DEPTH ) return; - const auto& proxy = get_account( a.proxy ); + const auto& proxy = get_account( a.get_proxy() ); modify( proxy, [&]( account_object& a ) { @@ -1728,13 +1697,13 @@ void database::adjust_proxied_witness_votes( const account_object& a, void database::adjust_proxied_witness_votes( const account_object& a, share_type delta, int depth ) { - if( a.proxy != HIVE_PROXY_TO_SELF_ACCOUNT ) + if( a.has_proxy() ) { /// nested proxies are not supported, vote will not propagate if( depth >= HIVE_MAX_PROXY_RECURSION_DEPTH ) return; - const auto& proxy = get_account( a.proxy ); + const auto& proxy = get_account( a.get_proxy() ); modify( proxy, [&]( account_object& a ) { @@ -1749,7 +1718,16 @@ void database::adjust_proxied_witness_votes( const account_object& a, share_type } } -void database::adjust_witness_votes( const account_object& a, share_type delta ) +void database::nullify_proxied_witness_votes( const account_object& a ) +{ + std::array delta; + delta[ 0 ] = -a.get_real_vesting_shares(); + for( int i = 0; i < HIVE_MAX_PROXY_RECURSION_DEPTH; ++i ) + delta[ i + 1 ] = -a.proxied_vsf_votes[ i ]; + adjust_proxied_witness_votes( a, delta ); +} + +void database::adjust_witness_votes( const account_object& a, const share_type& delta ) { const auto& vidx = get_index< witness_vote_index >().indices().get< by_account_witness >(); auto itr = vidx.lower_bound( boost::make_tuple( a.name, account_name_type() ) ); @@ -2064,7 +2042,8 @@ void database::lock_account( const account_object& account ) modify( account, []( account_object& a ) { - a.recovery_account = a.name; + a.set_recovery_account( a ); + a.memo_key = public_key_type(); } ); auto rec_req = find< account_recovery_request_object, by_account >( account.name ); @@ -2124,7 +2103,7 @@ void database::gather_balance( const std::string& name, const asset& balance, co void database::clear_accounts( const std::set< std::string >& cleared_accounts ) { auto treasury_name = get_treasury_name(); - for( auto account_name : cleared_accounts ) + for( const auto& account_name : cleared_accounts ) { const auto* account_ptr = find_account( account_name ); if( account_ptr == nullptr ) @@ -2270,16 +2249,24 @@ void database::clear_account( const account_object& account, // Remove pending convert requests (return balance to account) const auto& request_idx = get_index< chain::convert_request_index, chain::by_owner >(); - auto request_itr = request_idx.lower_bound( account_name ); - while( request_itr != request_idx.end() && request_itr->owner == account_name ) + auto request_itr = request_idx.lower_bound( account.get_id() ); + while( request_itr != request_idx.end() && request_itr->get_owner() == account.get_id() ) { auto& request = *request_itr; ++request_itr; - adjust_balance( account, request.amount ); + adjust_balance( account, request.get_convert_amount() ); remove( request ); } + // Make sure there are no pending collateralized convert requests + // (if we decided to handle them anyway, in case we wanted to reuse this routine outsede HF23 code, + // we should most likely destroy collateral balance instead of putting it into treasury) + const auto& collateralized_request_idx = get_index< chain::collateralized_convert_request_index, chain::by_owner >(); + auto collateralized_request_itr = collateralized_request_idx.lower_bound( account.get_id() ); + FC_ASSERT( collateralized_request_itr == collateralized_request_idx.end() || collateralized_request_itr->get_owner() != account.get_id(), + "Collateralized convert requests not handled by clear_account" ); + // Remove ongoing saving withdrawals (return/pass balance to account) const auto& withdraw_from_idx = get_index< savings_withdraw_index, by_from_rid >(); auto withdraw_from_itr = withdraw_from_idx.lower_bound( account_name ); @@ -2390,6 +2377,110 @@ void database::process_delayed_voting( const block_notification& note ) } } +/** + * Iterates over all recurrent transfers with a due date date before + * the head block time and then executes the transfers + */ +void database::process_recurrent_transfers() +{ + if( !has_hardfork( HIVE_HARDFORK_1_25 ) ) + return; + + auto now = head_block_time(); + const auto& recurrent_transfers_by_date = get_index< recurrent_transfer_index, by_trigger_date >(); + auto itr = recurrent_transfers_by_date.begin(); + + // uint16_t is okay because we stop at 1000, if the limit changes, make sure to check if it fits in the integer. + uint16_t processed_transfers = 0; + + while( itr != recurrent_transfers_by_date.end() && itr->get_trigger_date() <= now ) + { + // Since this is an intensive process, we don't want to process too many recurrent transfers in a single block + if (processed_transfers >= HIVE_MAX_RECURRENT_TRANSFERS_PER_BLOCK) + { + ilog("Reached max processed recurrent transfers this block"); + return; + } + + auto ¤t_recurrent_transfer = *itr; + ++itr; + + const auto &from_account = get_account(current_recurrent_transfer.from_id); + const auto &to_account = get_account(current_recurrent_transfer.to_id); + asset available = get_balance(from_account, current_recurrent_transfer.amount.symbol); + FC_ASSERT(current_recurrent_transfer.remaining_executions > 0); + const auto remaining_executions = current_recurrent_transfer.remaining_executions -1; + bool remove_recurrent_transfer = false; + + // If we have enough money, we proceed with the transfer + if (available >= current_recurrent_transfer.amount) + { + adjust_balance(from_account, -current_recurrent_transfer.amount); + adjust_balance(to_account, current_recurrent_transfer.amount); + + // No need to update the object if we know that we will remove it + if (remaining_executions == 0) + { + remove_recurrent_transfer = true; + } + else + { + modify(current_recurrent_transfer, [&](recurrent_transfer_object &rt) + { + rt.consecutive_failures = 0; // reset the consecutive failures counter + rt.update_next_trigger_date(); + rt.remaining_executions = remaining_executions; + }); + } + + push_virtual_operation(fill_recurrent_transfer_operation(from_account.name, to_account.name, current_recurrent_transfer.amount, to_string(current_recurrent_transfer.memo), remaining_executions)); + } + else + { + uint8_t consecutive_failures = current_recurrent_transfer.consecutive_failures + 1; + + if (consecutive_failures < HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES) + { + // No need to update the object if we know that we will remove it + if (remaining_executions == 0) + { + remove_recurrent_transfer = true; + } + else + { + modify(current_recurrent_transfer, [&](recurrent_transfer_object &rt) + { + ++rt.consecutive_failures; + rt.update_next_trigger_date(); + rt.remaining_executions = remaining_executions; + }); + } + // false means the recurrent transfer was not deleted + push_virtual_operation(failed_recurrent_transfer_operation(from_account.name, to_account.name, current_recurrent_transfer.amount, consecutive_failures, to_string(current_recurrent_transfer.memo), remaining_executions, false)); + } + else + { + // if we had too many consecutive failures, remove the recurrent payment object + remove_recurrent_transfer = true; + // true means the recurrent transfer was deleted + push_virtual_operation(failed_recurrent_transfer_operation(from_account.name, to_account.name, current_recurrent_transfer.amount, consecutive_failures, to_string(current_recurrent_transfer.memo), remaining_executions, true)); + } + } + + if (remove_recurrent_transfer) + { + remove( current_recurrent_transfer ); + modify(from_account, [&](account_object& a ) + { + FC_ASSERT( a.open_recurrent_transfers > 0 ); + a.open_recurrent_transfers--; + }); + } + + processed_transfers++; + } +} + /** * This method updates total_reward_shares2 on DGPO, and children_rshares2 on comments, when a comment's rshares2 changes * from old_rshares2 to new_rshares2. Maintaining invariants that children_rshares2 is the sum of all descendants' rshares2, @@ -2646,7 +2737,7 @@ share_type database::pay_curators( const comment_object& comment, const comment_ { unclaimed_rewards -= claim; const auto& voter = get( item->voter ); - operation vop = curation_reward_operation( voter.name, asset(0, VESTS_SYMBOL), comment_author_name, to_string( comment_cashout.permlink ) ); + operation vop = curation_reward_operation( voter.name, asset(0, VESTS_SYMBOL), comment_author_name, to_string( comment_cashout.permlink ), has_hardfork( HIVE_HARDFORK_0_17__659 ) ); create_vesting2( *this, voter, asset( claim, HIVE_SYMBOL ), has_hardfork( HIVE_HARDFORK_0_17__659 ), [&]( const asset& reward ) { @@ -2694,11 +2785,13 @@ share_type database::cashout_comment_helper( util::comment_reward_context& ctx, const share_type reward = util::get_rshare_reward( ctx ); uint128_t reward_tokens = uint128_t( reward.value ); + share_type curation_tokens; + share_type author_tokens; if( reward_tokens > 0 ) { - share_type curation_tokens = ( ( reward_tokens * get_curation_rewards_percent() ) / HIVE_100_PERCENT ).to_uint64(); - share_type author_tokens = reward_tokens.to_uint64() - curation_tokens; + curation_tokens = ( ( reward_tokens * get_curation_rewards_percent() ) / HIVE_100_PERCENT ).to_uint64(); + author_tokens = reward_tokens.to_uint64() - curation_tokens; share_type curation_remainder = pay_curators( comment, comment_cashout, curation_tokens ); @@ -2759,7 +2852,7 @@ share_type database::cashout_comment_helper( util::comment_reward_context& ctx, auto curators_vesting_payout = calculate_vesting( *this, asset( curation_tokens, HIVE_SYMBOL ), has_hardfork( HIVE_HARDFORK_0_17__659 ) ); operation vop = author_reward_operation( comment_author, to_string( comment_cashout.permlink ), hbd_payout.first, hbd_payout.second, asset( 0, VESTS_SYMBOL ), - curators_vesting_payout ); + curators_vesting_payout, has_hardfork( HIVE_HARDFORK_0_17__659 ) ); create_vesting2( *this, author, asset( vesting_hive, HIVE_SYMBOL ), has_hardfork( HIVE_HARDFORK_0_17__659 ), [&]( const asset& vesting_payout ) @@ -2788,6 +2881,8 @@ share_type database::cashout_comment_helper( util::comment_reward_context& ctx, }); } + notify_comment_reward( { reward, author_tokens, curation_tokens } ); + if( !has_hardfork( HIVE_HARDFORK_0_17__774 ) ) adjust_rshares2( util::evaluate_reward_curve( comment_cashout.net_rshares.value ), 0 ); } @@ -2841,9 +2936,7 @@ share_type database::cashout_comment_helper( util::comment_reward_context& ctx, } else { -#ifdef CLEAR_VOTES remove( cur_vote ); -#endif } } @@ -3035,7 +3128,10 @@ void database::process_funds() else if( cwit.schedule == witness_object::elected ) witness_reward *= wso.elected_weight; else - wlog( "Encountered unknown witness type for witness: ${w}", ("w", cwit.owner) ); + { + push_virtual_operation( system_warning_operation( FC_LOG_MESSAGE( warn, + "Encountered unknown witness type for witness: ${w}", ( "w", cwit.owner ) ).get_message() ) ); + } witness_reward /= wso.witness_pay_normalization_factor; @@ -3142,7 +3238,7 @@ asset database::get_liquidity_reward()const return asset( 0, HIVE_SYMBOL ); const auto& props = get_dynamic_global_properties(); - static_assert( HIVE_LIQUIDITY_REWARD_PERIOD_SEC == 60*60, "this code assumes a 1 hour time interval" ); + static_assert( HIVE_LIQUIDITY_REWARD_PERIOD_SEC == 60*60, "this code assumes a 1 hour time interval" ); // NOLINT(misc-redundant-expression) asset percent( protocol::calc_percent_reward_per_hour< HIVE_LIQUIDITY_APR_PERCENT >( props.virtual_supply.amount ), HIVE_SYMBOL ); return std::max( percent, HIVE_MIN_LIQUIDITY_REWARD ); } @@ -3190,6 +3286,7 @@ asset database::get_producer_reward() { a.balance += pay; } ); + push_virtual_operation( producer_reward_operation( witness_account.name, pay ) ); } return pay; @@ -3255,7 +3352,7 @@ uint16_t database::get_curation_rewards_percent() const return HIVE_1_PERCENT * 50; } -share_type database::pay_reward_funds( share_type reward ) +share_type database::pay_reward_funds( const share_type& reward ) { const auto& reward_idx = get_index< reward_fund_index, by_id >(); share_type used_rewards = 0; @@ -3280,16 +3377,14 @@ share_type database::pay_reward_funds( share_type reward ) } /** - * Iterates over all conversion requests with a conversion date before + * Iterates over all [collateralized] conversion requests with a conversion date before * the head block time and then converts them to/from HIVE/HBD at the - * current median price feed history price times the premium + * current median price feed history price times the premium/fee + * Collateralized requests might also return excess collateral. */ void database::process_conversions() { auto now = head_block_time(); - const auto& request_by_date = get_index< convert_request_index >().indices().get< by_conversion_date >(); - auto itr = request_by_date.begin(); - const auto& fhistory = get_feed_history(); if( fhistory.current_median_history.is_null() ) return; @@ -3297,29 +3392,82 @@ void database::process_conversions() asset net_hbd( 0, HBD_SYMBOL ); asset net_hive( 0, HIVE_SYMBOL ); - while( itr != request_by_date.end() && itr->conversion_date <= now ) + //regular requests { - auto amount_to_issue = itr->amount * fhistory.current_median_history; + const auto& request_by_date = get_index< convert_request_index, by_conversion_date >(); + auto itr = request_by_date.begin(); - adjust_balance( itr->owner, amount_to_issue ); + while( itr != request_by_date.end() && itr->get_conversion_date() <= now ) + { + auto amount_to_issue = itr->get_convert_amount() * fhistory.current_median_history; + const auto& owner = get_account( itr->get_owner() ); - net_hbd += itr->amount; - net_hive += amount_to_issue; + adjust_balance( owner, amount_to_issue ); - push_virtual_operation( fill_convert_request_operation ( itr->owner, itr->requestid, itr->amount, amount_to_issue ) ); + net_hbd -= itr->get_convert_amount(); + net_hive += amount_to_issue; - remove( *itr ); - itr = request_by_date.begin(); + push_virtual_operation( fill_convert_request_operation( owner.get_name(), itr->get_request_id(), + itr->get_convert_amount(), amount_to_issue ) ); + + remove( *itr ); + itr = request_by_date.begin(); + } } - const auto& props = get_dynamic_global_properties(); - modify( props, [&]( dynamic_global_property_object& p ) + //collateralized requests + { + const auto& request_by_date = get_index< collateralized_convert_request_index, by_conversion_date >(); + auto itr = request_by_date.begin(); + + while( itr != request_by_date.end() && itr->get_conversion_date() <= now ) + { + const auto& owner = get_account( itr->get_owner() ); + + //calculate how much HIVE we'd need for already created HBD at current corrected price + //note that we are using market median price instead of minimal as it was during immediate part of this + //conversion or current median price as is used in other conversions; the former is by design - we are supposed + //to use median price here, minimal during immediate conversion was to prevent gaming; the latter is for rare + //case, when this conversion happens when median price does not reflect market conditions when hard limit was + //hit - see update_median_feed() + auto required_hive = multiply_with_fee( itr->get_converted_amount(), fhistory.market_median_history, + HIVE_COLLATERALIZED_CONVERSION_FEE, HIVE_SYMBOL ); + auto excess_collateral = itr->get_collateral_amount() - required_hive; + if( excess_collateral.amount < 0 ) + { + push_virtual_operation( system_warning_operation( FC_LOG_MESSAGE( warn, + "Insufficient collateral on conversion ${id} by ${o} - shortfall of ${ec}", + ( "id", itr->get_request_id() )( "o", owner.get_name() )( "ec", -excess_collateral ) ).get_message() ) ); + + required_hive = itr->get_collateral_amount(); + excess_collateral.amount = 0; + } + else + { + adjust_balance( owner, excess_collateral ); + } + + net_hive -= required_hive; + //note that HBD was created immediately, so we don't need to correct its supply here + push_virtual_operation( fill_collateralized_convert_request_operation( owner.get_name(), itr->get_request_id(), + required_hive, itr->get_converted_amount(), excess_collateral ) ); + + remove( *itr ); + itr = request_by_date.begin(); + } + } + + //correct global supply (if needed) + if( net_hive.amount.value | net_hbd.amount.value ) { + modify( get_dynamic_global_properties(), [&]( dynamic_global_property_object& p ) + { p.current_supply += net_hive; - p.current_hbd_supply -= net_hbd; + p.current_hbd_supply += net_hbd; p.virtual_supply += net_hive; - p.virtual_supply -= net_hbd * get_feed_history().current_median_history; - } ); + p.virtual_supply += net_hbd * fhistory.current_median_history; + } ); + } } asset database::to_hbd( const asset& hive )const @@ -3335,10 +3483,10 @@ asset database::to_hive( const asset& hbd )const void database::account_recovery_processing() { // Clear expired recovery requests - const auto& rec_req_idx = get_index< account_recovery_request_index >().indices().get< by_expiration >(); + const auto& rec_req_idx = get_index< account_recovery_request_index, by_expiration >(); auto rec_req = rec_req_idx.begin(); - while( rec_req != rec_req_idx.end() && rec_req->expires <= head_block_time() ) + while( rec_req != rec_req_idx.end() && rec_req->get_expiration_time() <= head_block_time() ) { remove( *rec_req ); rec_req = rec_req_idx.begin(); @@ -3355,16 +3503,21 @@ void database::account_recovery_processing() } // Apply effective recovery_account changes - const auto& change_req_idx = get_index< change_recovery_account_request_index >().indices().get< by_effective_date >(); + const auto& change_req_idx = get_index< change_recovery_account_request_index, by_effective_date >(); auto change_req = change_req_idx.begin(); - while( change_req != change_req_idx.end() && change_req->effective_on <= head_block_time() ) + while( change_req != change_req_idx.end() && change_req->get_execution_time() <= head_block_time() ) { - modify( get_account( change_req->account_to_recover ), [&]( account_object& a ) + const auto& account = get_account( change_req->get_account_to_recover() ); + const auto& old_recovery_account_name = account.get_recovery_account(); + const auto& new_recovery_account = get_account( change_req->get_recovery_account() ); + modify( account, [&]( account_object& a ) { - a.recovery_account = change_req->recovery_account; + a.set_recovery_account( new_recovery_account ); }); + push_virtual_operation(changed_recovery_account_operation( account.name, old_recovery_account_name, new_recovery_account.name )); + remove( *change_req ); change_req = change_req_idx.begin(); } @@ -3402,19 +3555,13 @@ void database::process_decline_voting_rights() { const auto& account = get< account_object, by_name >( itr->account ); - /// remove all current votes - std::array delta; - delta[0] = -account.get_real_vesting_shares(); - for( int i = 0; i < HIVE_MAX_PROXY_RECURSION_DEPTH; ++i ) - delta[i+1] = -account.proxied_vsf_votes[i]; - adjust_proxied_witness_votes( account, delta ); - + nullify_proxied_witness_votes( account ); clear_witness_votes( account ); modify( account, [&]( account_object& a ) { a.can_vote = false; - a.proxy = HIVE_PROXY_TO_SELF_ACCOUNT; + a.clear_proxy(); }); remove( *itr ); @@ -3442,9 +3589,21 @@ node_property_object& database::node_properties() return _node_property_object; } -uint32_t database::last_non_undoable_block_num() const +uint32_t database::get_last_irreversible_block_num() const { - return get_dynamic_global_properties().last_irreversible_block_num; + //ilog("getting last_irreversible_block_num irreversible is ${l}", ("l", irreversible_object->last_irreversible_block_num)); + //ilog("getting last_irreversible_block_num head is ${l}", ("l", head_block_num())); + return irreversible_object->last_irreversible_block_num; +} + +void database::set_last_irreversible_block_num(uint32_t block_num) +{ + //ilog("setting last_irreversible_block_num previous ${l}", ("l", irreversible_object->last_irreversible_block_num)); + FC_ASSERT(block_num >= irreversible_object->last_irreversible_block_num, "Irreversible block can only move forward. Old: ${o}, new: ${n}", + ("o", irreversible_object->last_irreversible_block_num)("n", block_num)); + + irreversible_object->last_irreversible_block_num = block_num; + //ilog("setting last_irreversible_block_num new ${l}", ("l", irreversible_object->last_irreversible_block_num)); } void database::initialize_evaluators() @@ -3471,6 +3630,7 @@ void database::initialize_evaluators() _my->_evaluator_registry.register_evaluator< report_over_production_evaluator >(); _my->_evaluator_registry.register_evaluator< feed_publish_evaluator >(); _my->_evaluator_registry.register_evaluator< convert_evaluator >(); + _my->_evaluator_registry.register_evaluator< collateralized_convert_evaluator >(); _my->_evaluator_registry.register_evaluator< limit_order_create_evaluator >(); _my->_evaluator_registry.register_evaluator< limit_order_create2_evaluator >(); _my->_evaluator_registry.register_evaluator< limit_order_cancel_evaluator >(); @@ -3510,6 +3670,7 @@ void database::initialize_evaluators() _my->_evaluator_registry.register_evaluator< update_proposal_evaluator >(); _my->_evaluator_registry.register_evaluator< update_proposal_votes_evaluator >(); _my->_evaluator_registry.register_evaluator< remove_proposal_evaluator >(); + _my->_evaluator_registry.register_evaluator< recurrent_transfer_evaluator >(); #ifdef IS_TEST_NET @@ -3544,6 +3705,13 @@ void database::initialize_indexes() _plugin_index_signal(); } +void database::initialize_irreversible_storage() +{ + auto s = get_segment_manager(); + + irreversible_object = s->find_or_construct( "irreversible" )(); +} + void database::resetState(const open_args& args) { wipe(args.data_dir, args.shared_mem_dir, false); @@ -3696,6 +3864,9 @@ void database::init_genesis( uint64_t init_supply, uint64_t hbd_init_supply ) create< feed_history_object >( [&]( feed_history_object& o ) { o.current_median_history = price( asset( 1, HBD_SYMBOL ), asset( 1, HIVE_SYMBOL ) ); + o.market_median_history = o.current_median_history; + o.current_min_history = o.current_median_history; + o.current_max_history = o.current_median_history; }); #else // Nothing to do @@ -3843,7 +4014,6 @@ void database::apply_block( const signed_block& next_block, uint32_t skip ) void database::check_free_memory( bool force_print, uint32_t current_block_num ) { -#ifndef ENABLE_MIRA uint64_t free_mem = get_free_memory(); uint64_t max_mem = get_max_memory(); @@ -3879,7 +4049,6 @@ void database::check_free_memory( bool force_print, uint32_t current_block_num ) elog( "Free memory is now ${n}M. Increase shared file size immediately!" , ("n", free_mb) ); } } -#endif } void database::_apply_block( const signed_block& next_block ) @@ -4014,22 +4183,17 @@ void database::_apply_block( const signed_block& next_block ) update_global_dynamic_data(next_block); update_signing_witness(signing_witness, next_block); - update_last_irreversible_block(); + uint32_t old_last_irreversible = update_last_irreversible_block(); create_block_summary(next_block); clear_expired_transactions(); clear_expired_orders(); clear_expired_delegations(); - if( next_block.block_num() % 100000 == 0 ) - { - - } - update_witness_schedule(*this); update_median_feed(); - update_virtual_supply(); + update_virtual_supply(); //accommodate potentially new price clear_null_account_balance(); consolidate_treasury_balance(); @@ -4040,13 +4204,16 @@ void database::_apply_block( const signed_block& next_block ) process_savings_withdraws(); process_subsidized_accounts(); pay_liquidity_reward(); - update_virtual_supply(); + update_virtual_supply(); //cover changes in HBD supply from above processes account_recovery_processing(); expire_escrow_ratification(); process_decline_voting_rights(); - process_proposals( note ); + process_proposals( note ); //new HBD converted here does not count towards limit process_delayed_voting( note ); + remove_expired_governance_votes(); + + process_recurrent_transfers(); generate_required_actions(); generate_optional_actions(); @@ -4065,8 +4232,7 @@ void database::_apply_block( const signed_block& next_block ) // and commits irreversible state to the database. This should always be the // last call of applying a block because it is the only thing that is not // reversible. - migrate_irreversible_state(); - trim_cache(); + migrate_irreversible_state(old_last_irreversible); } FC_CAPTURE_CALL_LOG_AND_RETHROW( std::bind( &database::notify_fail_apply_block, this, note ), (next_block.block_num()) ) } @@ -4141,7 +4307,8 @@ void database::process_header_extensions( const signed_block& next_block, requir e.visit( _v ); } -void database::update_median_feed() { +void database::update_median_feed() +{ try { if( (head_block_num() % HIVE_FEED_INTERVAL_BLOCKS) != 0 ) return; @@ -4184,15 +4351,12 @@ try { if( fho.price_history.size() ) { - /// BW-TODO Why deque is used here ? Also why don't make copy of whole container ? - std::deque< price > copy; - for( const auto& i : fho.price_history ) - { - copy.push_back( i ); - } - - std::sort( copy.begin(), copy.end() ); /// TODO: use nth_item + std::vector< price > copy( fho.price_history.begin(), fho.price_history.end() ); + std::sort( copy.begin(), copy.end() ); fho.current_median_history = copy[copy.size()/2]; + fho.market_median_history = fho.current_median_history; + fho.current_min_history = copy.front(); + fho.current_max_history = copy.back(); #ifdef IS_TEST_NET if( skip_price_feed_limit_check ) @@ -4201,21 +4365,34 @@ try { if( has_hardfork( HIVE_HARDFORK_0_14__230 ) ) { // This block limits the effective median price to force HBD to remain at or - // below 10% of the combined market cap of HIVE and HBD. + // below 10% of the combined market cap of HIVE and HBD. The reason is to prevent + // individual with a lot of HBD to use sharp decline in HIVE price to make + // in-chain-but-out-of-market conversion to HIVE and take over the blockchain // // For example, if we have 500 HIVE and 100 HBD, the price is limited to // 900 HBD / 500 HIVE which works out to be $1.80. At this price, 500 HIVE // would be valued at 500 * $1.80 = $900. 100 HBD is by definition always $100, // so the combined market cap is $900 + $100 = $1000. - const auto& gpo = get_dynamic_global_properties(); - - if( gpo.get_current_hbd_supply().amount > 0 ) + const auto& dgpo = get_dynamic_global_properties(); + auto hbd_supply = dgpo.get_current_hbd_supply(); + if( has_hardfork( HIVE_HARDFORK_1_25_HBD_HARD_CAP ) ) + hbd_supply -= get_treasury().get_hbd_balance(); + if( hbd_supply.amount > 0 ) { - price min_price( asset( 9 * gpo.get_current_hbd_supply().amount, HBD_SYMBOL ), gpo.current_supply ); + price min_price( asset( 9 * hbd_supply.amount, HBD_SYMBOL ), dgpo.get_current_supply() ); if( min_price > fho.current_median_history ) + { + push_virtual_operation( system_warning_operation( FC_LOG_MESSAGE( warn, + "HIVE price corrected upward due to 10% HBD cutoff rule, from ${actual} to ${corrected}", + ( "actual", fho.current_median_history )( "corrected", min_price ) ).get_message() ) ); + fho.current_median_history = min_price; + } + //should we also correct current_min_history? note that we MUST NOT change market_median_history + if( min_price > fho.current_max_history ) + fho.current_max_history = min_price; } } } @@ -4430,7 +4607,7 @@ void database::process_optional_actions( const optional_automated_actions& actio // and it is safe to delete. const auto& pending_action_idx = get_index< pending_optional_action_index, by_execution >(); auto pending_itr = pending_action_idx.begin(); - auto lib = fetch_block_by_number( get_dynamic_global_properties().last_irreversible_block_num ); + auto lib = fetch_block_by_number( get_last_irreversible_block_num() ); // This is always valid when running on mainnet because there are irreversible blocks // Testnet and unit tests, not so much. Could be ifdeffed with IS_TEST_NET, but seems @@ -4639,6 +4816,11 @@ boost::signals2::connection database::add_snapshot_supplement_handler(const load return connect_impl(_load_snapshot_supplement_signal, func, plugin, group, "->load_snapshot_data_supplement"); } +boost::signals2::connection database::add_comment_reward_handler(const comment_reward_notification_handler_t& func, const abstract_plugin& plugin, int32_t group) +{ + return connect_impl(_comment_reward_signal, func, plugin, group, "->comment_reward"); +} + const witness_object& database::validate_block_header( uint32_t skip, const signed_block& next_block )const { try { FC_ASSERT( head_block_id() == next_block.previous, "", ("head_block_id",head_block_id())("next.prev",next_block.previous) ); @@ -4727,45 +4909,50 @@ FC_TODO( "#ifndef not needed after HF 20 is live" ); if( !(get_node_properties().skip_flags & skip_undo_history_check) ) { - HIVE_ASSERT( _dgp.head_block_number - _dgp.last_irreversible_block_num < HIVE_MAX_UNDO_HISTORY, undo_database_exception, + HIVE_ASSERT( _dgp.head_block_number - get_last_irreversible_block_num() < HIVE_MAX_UNDO_HISTORY, undo_database_exception, "The database does not have enough undo history to support a blockchain with so many missed blocks. " "Please add a checkpoint if you would like to continue applying blocks beyond this point.", - ("last_irreversible_block_num",_dgp.last_irreversible_block_num)("head", _dgp.head_block_number) + ("last_irreversible_block_num",get_last_irreversible_block_num())("head", _dgp.head_block_number) ("max_undo",HIVE_MAX_UNDO_HISTORY) ); } } FC_CAPTURE_AND_RETHROW() } +uint16_t database::calculate_HBD_percent() +{ + auto median_price = get_feed_history().current_median_history; + if( median_price.is_null() ) + return 0; + + const auto& dgpo = get_dynamic_global_properties(); + auto hbd_supply = dgpo.get_current_hbd_supply(); + auto virtual_supply = dgpo.virtual_supply; + if( has_hardfork( HIVE_HARDFORK_1_24 ) ) + { + // Removing the hbd in the treasury from the debt ratio calculations + hbd_supply -= get_treasury().get_hbd_balance(); + if( hbd_supply.amount < 0 ) + hbd_supply = asset( 0, HBD_SYMBOL ); + virtual_supply = hbd_supply * median_price + dgpo.get_current_supply(); + } + + auto hbd_as_hive = fc::uint128_t( ( hbd_supply * median_price ).amount.value ); + hbd_as_hive *= HIVE_100_PERCENT; + if( has_hardfork( HIVE_HARDFORK_0_21 ) ) + hbd_as_hive += virtual_supply.amount.value / 2; // added rounding + return uint16_t( ( hbd_as_hive / virtual_supply.amount.value ).to_uint64() ); +} + void database::update_virtual_supply() { try { modify( get_dynamic_global_properties(), [&]( dynamic_global_property_object& dgp ) { - dgp.virtual_supply = dgp.current_supply - + ( get_feed_history().current_median_history.is_null() ? asset( 0, HIVE_SYMBOL ) : dgp.get_current_hbd_supply() * get_feed_history().current_median_history ); - auto median_price = get_feed_history().current_median_history; + dgp.virtual_supply = dgp.current_supply + + ( median_price.is_null() ? asset( 0, HIVE_SYMBOL ) : dgp.get_current_hbd_supply() * median_price ); if( !median_price.is_null() && has_hardfork( HIVE_HARDFORK_0_14__230 ) ) { - uint16_t percent_hbd = 0; - - if( has_hardfork( HIVE_HARDFORK_1_24 ) ) - { - // Removing the hbd in the treasury from the debt ratio calculations - const auto &treasury_account = get_treasury(); - const auto hdb_supply_without_treasury = (dgp.get_current_hbd_supply() - treasury_account.hbd_balance).amount < 0 ? asset(0, HBD_SYMBOL) : (dgp.get_current_hbd_supply() - treasury_account.hbd_balance) ; - const auto virtual_supply_without_treasury = hdb_supply_without_treasury * get_feed_history().current_median_history + dgp.current_supply; - percent_hbd = uint16_t( ( ( fc::uint128_t( ( hdb_supply_without_treasury * get_feed_history().current_median_history ).amount.value ) * HIVE_100_PERCENT + virtual_supply_without_treasury.amount.value/2 ) - / virtual_supply_without_treasury.amount.value ).to_uint64() ); - } else if( has_hardfork( HIVE_HARDFORK_0_21 ) ) - { - percent_hbd = uint16_t( ( ( fc::uint128_t( ( dgp.get_current_hbd_supply() * get_feed_history().current_median_history ).amount.value ) * HIVE_100_PERCENT + dgp.virtual_supply.amount.value/2 ) - / dgp.virtual_supply.amount.value ).to_uint64() ); - } - else - { - percent_hbd = uint16_t( ( ( fc::uint128_t( ( dgp.get_current_hbd_supply() * get_feed_history().current_median_history ).amount.value ) * HIVE_100_PERCENT ) - / dgp.virtual_supply.amount.value ).to_uint64() ); - } + uint16_t percent_hbd = calculate_HBD_percent(); if( percent_hbd <= dgp.hbd_start_percent ) dgp.hbd_print_rate = HIVE_100_PERCENT; @@ -4789,10 +4976,9 @@ void database::update_signing_witness(const witness_object& signing_witness, con } ); } FC_CAPTURE_AND_RETHROW() } -void database::update_last_irreversible_block() +uint32_t database::update_last_irreversible_block() { try { - const dynamic_global_property_object& dpo = get_dynamic_global_properties(); - auto old_last_irreversible = dpo.last_irreversible_block_num; + uint32_t old_last_irreversible = get_last_irreversible_block_num(); /** * Prior to voting taking over, we must be more conservative... @@ -4800,11 +4986,12 @@ void database::update_last_irreversible_block() */ if( head_block_num() < HIVE_START_MINER_VOTING_BLOCK ) { - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - if ( head_block_num() > HIVE_MAX_WITNESSES ) - _dpo.last_irreversible_block_num = head_block_num() - HIVE_MAX_WITNESSES; - } ); + if ( head_block_num() > HIVE_MAX_WITNESSES ) { + uint32_t new_last_irreversible_block_num = head_block_num() - HIVE_MAX_WITNESSES; + + if( new_last_irreversible_block_num > get_last_irreversible_block_num() ) + set_last_irreversible_block_num(new_last_irreversible_block_num); + } } else { @@ -4831,22 +5018,16 @@ void database::update_last_irreversible_block() uint32_t new_last_irreversible_block_num = wit_objs[offset]->last_confirmed_block_num; - if( new_last_irreversible_block_num > dpo.last_irreversible_block_num ) + if( new_last_irreversible_block_num > get_last_irreversible_block_num() ) { - modify( dpo, [&]( dynamic_global_property_object& _dpo ) - { - _dpo.last_irreversible_block_num = new_last_irreversible_block_num; - } ); + //ilog("Last irreversible block changed to ${b}. Got from witness: ${w}", ("b", new_last_irreversible_block_num)("w", wit_objs[offset]->owner)); + set_last_irreversible_block_num(new_last_irreversible_block_num); } } - - for( uint32_t i = old_last_irreversible; i <= dpo.last_irreversible_block_num; ++i ) - { - notify_irreversible_block( i ); - } + return old_last_irreversible; } FC_CAPTURE_AND_RETHROW() } -void database::migrate_irreversible_state() +void database::migrate_irreversible_state(uint32_t old_last_irreversible) { // This method should happen atomically. We cannot prevent unclean shutdown in the middle // of the call, but all side effects happen at the end to minize the chance that state @@ -4871,11 +5052,11 @@ void database::migrate_irreversible_state() if( tmp_head ) log_head_num = tmp_head->block_num(); - if( log_head_num < dpo.last_irreversible_block_num ) + if( log_head_num < get_last_irreversible_block_num() ) { // Check for all blocks that we want to write out to the block log but don't write any // unless we are certain they all exist in the fork db - while( log_head_num < dpo.last_irreversible_block_num ) + while( log_head_num < get_last_irreversible_block_num() ) { item_ptr block_ptr = _fork_db.fetch_block_on_main_branch_by_number( log_head_num+1 ); FC_ASSERT( block_ptr, "Current fork in the fork database does not contain the last_irreversible_block" ); @@ -4893,10 +5074,22 @@ void database::migrate_irreversible_state() } // This deletes blocks from the fork db - _fork_db.set_max_size( dpo.head_block_number - dpo.last_irreversible_block_num + 1 ); + _fork_db.set_max_size( dpo.head_block_number - get_last_irreversible_block_num() + 1 ); // This deletes undo state - commit( dpo.last_irreversible_block_num ); + commit( get_last_irreversible_block_num() ); + + if(old_last_irreversible < get_last_irreversible_block_num() ) + { + //ilog("Updating last irreversible block to: ${b}. Old last irreversible was: ${ob}.", + // ("b", get_last_irreversible_block_num())("ob", old_last_irreversible)); + + for( uint32_t i = old_last_irreversible + 1; i <= get_last_irreversible_block_num(); ++i ) + { + notify_irreversible_block( i ); + } + } + } FC_CAPTURE_AND_RETHROW() } @@ -5186,7 +5379,7 @@ void database::adjust_smt_balance( const account_object& owner, const asset& del bo = &new_balance_object; } - modify( *bo, std::move( modifier ) ); + modify( *bo, std::forward( modifier ) ); if( bo->is_empty() ) { // Zero balance is the same as non object balance at all. @@ -5210,11 +5403,11 @@ void database::modify_balance( const account_object& a, const asset& delta, bool } break; case HIVE_ASSET_NUM_HBD: - if( a.hbd_seconds_last_update != head_block_time() ) + /// Starting from HF 25 HBD interest will be paid only from saving balance. + if( has_hardfork(HIVE_HARDFORK_1_25) == false && a.hbd_seconds_last_update != head_block_time() ) { acnt.hbd_seconds += fc::uint128_t(a.get_hbd_balance().amount.value) * (head_block_time() - a.hbd_seconds_last_update).to_seconds(); acnt.hbd_seconds_last_update = head_block_time(); - if( acnt.hbd_seconds > 0 && (acnt.hbd_seconds_last_update - acnt.hbd_last_interest_payment).to_seconds() > HIVE_HBD_INTEREST_COMPOUND_INTERVAL_SEC ) { @@ -5294,26 +5487,6 @@ void database::modify_reward_balance( const account_object& a, const asset& valu }); } -void database::set_index_delegate( const std::string& n, index_delegate&& d ) -{ - _index_delegate_map[ n ] = std::move( d ); -} - -const index_delegate& database::get_index_delegate( const std::string& n ) -{ - return _index_delegate_map.at( n ); -} - -bool database::has_index_delegate( const std::string& n ) -{ - return _index_delegate_map.find( n ) != _index_delegate_map.end(); -} - -const index_delegate_map& database::index_delegates() -{ - return _index_delegate_map; -} - void database::adjust_balance( const account_object& a, const asset& delta ) { if ( delta.amount < 0 ) @@ -5625,10 +5798,13 @@ void database::init_hardforks() FC_ASSERT( HIVE_HARDFORK_1_24 == 24, "Invalid hardfork configuration" ); _hardfork_versions.times[ HIVE_HARDFORK_1_24 ] = fc::time_point_sec( HIVE_HARDFORK_1_24_TIME ); _hardfork_versions.versions[ HIVE_HARDFORK_1_24 ] = HIVE_HARDFORK_1_24_VERSION; -#ifdef IS_TEST_NET FC_ASSERT( HIVE_HARDFORK_1_25 == 25, "Invalid hardfork configuration" ); _hardfork_versions.times[ HIVE_HARDFORK_1_25 ] = fc::time_point_sec( HIVE_HARDFORK_1_25_TIME ); _hardfork_versions.versions[ HIVE_HARDFORK_1_25 ] = HIVE_HARDFORK_1_25_VERSION; +#if defined(IS_TEST_NET) && defined(HIVE_ENABLE_SMT) + FC_ASSERT( HIVE_HARDFORK_1_26 == 26, "Invalid hardfork configuration" ); + _hardfork_versions.times[ HIVE_HARDFORK_1_26 ] = fc::time_point_sec( HIVE_HARDFORK_1_26_TIME ); + _hardfork_versions.versions[ HIVE_HARDFORK_1_26 ] = HIVE_HARDFORK_1_26_VERSION; #endif const auto& hardforks = get_hardfork_property_object(); @@ -6019,7 +6195,26 @@ void database::apply_hardfork( uint32_t hardfork ) case HIVE_HARDFORK_1_24: { restore_accounts( hardforkprotect::get_restored_accounts() ); +#ifdef IS_TEST_NET + /// Don't change chain_id in testnet build. +#else set_chain_id(HIVE_CHAIN_ID); +#endif /// IS_TEST_NET + break; + } + case HIVE_HARDFORK_1_25: + { + modify( get< reward_fund_object, by_name >( HIVE_POST_REWARD_FUND_NAME ), [&]( reward_fund_object& rfo ) + { + rfo.curation_reward_curve = linear; + rfo.author_reward_curve = linear; + }); + modify( get_dynamic_global_properties(), [&]( dynamic_global_property_object& gpo ) + { + gpo.reverse_auction_seconds = 0; + gpo.early_voting_seconds = HIVE_EARLY_VOTING_SECONDS_HF25; + gpo.mid_voting_seconds = HIVE_MID_VOTING_SECONDS_HF25; + }); break; } case HIVE_SMT_HARDFORK: @@ -6087,6 +6282,7 @@ void database::validate_invariants()const uint64_t witness_no = 0; uint64_t account_no = 0; uint64_t convert_no = 0; + uint64_t collateralized_convert_no = 0; uint64_t order_no = 0; uint64_t escrow_no = 0; uint64_t withdrawal_no = 0; @@ -6114,7 +6310,7 @@ void database::validate_invariants()const total_vesting += itr->get_vesting(); total_vesting += itr->get_vest_rewards(); pending_vesting_hive += itr->get_vest_rewards_as_hive(); - total_vsf_votes += ( itr->proxy == HIVE_PROXY_TO_SELF_ACCOUNT ? + total_vsf_votes += ( !itr->has_proxy() ? itr->witness_vote_weight() : ( HIVE_MAX_PROXY_RECURSION_DEPTH > 0 ? itr->proxied_vsf_votes[HIVE_MAX_PROXY_RECURSION_DEPTH - 1] : @@ -6129,18 +6325,20 @@ void database::validate_invariants()const } const auto& convert_request_idx = get_index< convert_request_index >().indices(); - for( auto itr = convert_request_idx.begin(); itr != convert_request_idx.end(); ++itr ) { - if( itr->amount.symbol == HIVE_SYMBOL ) - total_supply += itr->amount; - else if( itr->amount.symbol == HBD_SYMBOL ) - total_hbd += itr->amount; - else - FC_ASSERT( false, "Encountered illegal symbol in convert_request_object" ); + total_hbd += itr->get_convert_amount(); ++convert_no; } + const auto& collateralized_convert_request_idx = get_index< collateralized_convert_request_index >().indices(); + for( auto itr = collateralized_convert_request_idx.begin(); itr != collateralized_convert_request_idx.end(); ++itr ) + { + total_supply += itr->get_collateral_amount(); + // don't collect get_converted_amount() - it is not balance object; that tokens are already on owner's balance + ++collateralized_convert_no; + } + const auto& limit_order_idx = get_index< limit_order_index >().indices(); for( auto itr = limit_order_idx.begin(); itr != limit_order_idx.end(); ++itr ) @@ -6219,8 +6417,8 @@ void database::validate_invariants()const } ilog( "validate_invariants @${b}:", ( "b", head_block_num() ) ); - ilog( "successful scan of ${p} witnesses, ${a} accounts, ${c} convert requests, ${o} limit orders, ${e} escrow transfers, ${w} saving withdrawals, ${r} reward funds and ${s} SMT contributions.", - ( "p", witness_no )( "a", account_no )( "c", convert_no )( "o", order_no )( "e", escrow_no )( "w", withdrawal_no )( "r", reward_fund_no )( "s", contribution_no ) ); + ilog( "successful scan of ${p} witnesses, ${a} accounts, ${c} convert requests, ${cc} collateralized convert requests, ${o} limit orders, ${e} escrow transfers, ${w} saving withdrawals, ${r} reward funds and ${s} SMT contributions.", + ( "p", witness_no )( "a", account_no )( "c", convert_no )( "cc", collateralized_convert_no )( "o", order_no )( "e", escrow_no )( "w", withdrawal_no )( "r", reward_fund_no )( "s", contribution_no ) ); ilog( "HIVE supply: ${h}", ( "h", gpo.current_supply.amount.value ) ); ilog( "HBD supply: ${s} ( + ${i} initial )", ( "s", gpo.get_current_hbd_supply().amount.value )( "i", gpo.get_init_hbd_supply().amount.value ) ); ilog( "virtual supply (HIVE): ${w}", ( "w", gpo.virtual_supply.amount.value ) ); @@ -6363,9 +6561,12 @@ void database::perform_vesting_share_split( uint32_t magnitude ) // Need to update all VESTS in accounts and the total VESTS in the dgpo for( const auto& account : get_index< account_index, by_id >() ) { + asset old_vesting_shares = account.vesting_shares; + asset new_vesting_shares = account.vesting_shares; modify( account, [&]( account_object& a ) { a.vesting_shares.amount *= magnitude; + new_vesting_shares = a.vesting_shares; a.withdrawn *= magnitude; a.to_withdraw *= magnitude; a.vesting_withdraw_rate = asset( a.to_withdraw / HIVE_VESTING_WITHDRAW_INTERVALS_PRE_HF_16, VESTS_SYMBOL ); @@ -6375,6 +6576,8 @@ void database::perform_vesting_share_split( uint32_t magnitude ) for( uint32_t i = 0; i < HIVE_MAX_PROXY_RECURSION_DEPTH; ++i ) a.proxied_vsf_votes[i] *= magnitude; } ); + if (old_vesting_shares != new_vesting_shares) + push_virtual_operation( vesting_shares_split_operation(account.name, old_vesting_shares, new_vesting_shares) ); } const auto& comments = get_index< comment_cashout_index >().indices(); @@ -6416,8 +6619,6 @@ void database::retally_comment_children() { if( !itr->is_root() ) { -// Low memory nodes only need immediate child count, full nodes track total children -#ifdef IS_LOW_MEM const comment_cashout_object* comment_cashout = find_comment_cashout( itr->get_parent_id() ); if( comment_cashout ) { @@ -6426,26 +6627,6 @@ void database::retally_comment_children() c.children++; }); } -#else - const comment_object* parent = &get_comment( itr->get_parent_id() ); - while( parent ) - { - const comment_cashout_object* comment_cashout = find_comment_cashout( *parent ); - - if( comment_cashout ) - { - modify( *comment_cashout, [&]( comment_cashout_object& c ) - { - c.children++; - }); - } - - if( !parent->is_root() ) - parent = &get_comment( parent->get_parent_id() ); - else - parent = nullptr; - } -#endif } } } @@ -6469,7 +6650,7 @@ void database::retally_witness_votes() // Apply all existing votes by account for( auto itr = account_idx.begin(); itr != account_idx.end(); ++itr ) { - if( itr->proxy != HIVE_PROXY_TO_SELF_ACCOUNT ) continue; + if( itr->has_proxy() ) continue; const auto& a = *itr; @@ -6492,7 +6673,7 @@ void database::retally_witness_vote_counts( bool force ) { const auto& a = *itr; uint16_t witnesses_voted_for = 0; - if( force || (a.proxy != HIVE_PROXY_TO_SELF_ACCOUNT ) ) + if( force || a.has_proxy() ) { const auto& vidx = get_index< witness_vote_index >().indices().get< by_account_witness >(); auto wit_itr = vidx.lower_bound( boost::make_tuple( a.name, account_name_type() ) ); @@ -6517,5 +6698,46 @@ optional< chainbase::database::session >& database::pending_transaction_session( return _pending_tx_session; } +void database::remove_expired_governance_votes() +{ + if (!has_hardfork(HIVE_HARDFORK_1_25)) + return; + + const auto& accounts = get_index(); + auto acc_it = accounts.begin(); + time_point_sec first_expiring = acc_it->get_governance_vote_expiration_ts(); + time_point_sec now = head_block_time(); + + if( first_expiring > now ) + return; + + const auto& proposal_votes = get_index< proposal_vote_index, by_voter_proposal >(); + remove_guard obj_perf( get_remove_threshold() ); + + while( acc_it != accounts.end() && acc_it->get_governance_vote_expiration_ts() <= now ) + { + const auto& account = *acc_it; + ++acc_it; + + if( sps_helper::remove_proposal_votes( account, proposal_votes, *this, obj_perf ) ) + { + nullify_proxied_witness_votes( account ); + clear_witness_votes( account ); + modify( account, [&]( account_object& a ) + { + a.clear_proxy(); + a.set_governance_vote_expired(); + } ); + push_virtual_operation( expired_account_notification_operation( account.name ) ); + } + else + { + ilog("Threshold exceeded while processing account ${account} with expired governance vote.", + ("account", account.name)); // to be continued in next block + break; + } + } +} + } } //hive::chain diff --git a/libraries/chain/fork_database.cpp b/libraries/chain/fork_database.cpp index 6dcc194a9f..46c9e34c9b 100644 --- a/libraries/chain/fork_database.cpp +++ b/libraries/chain/fork_database.cpp @@ -43,8 +43,8 @@ shared_ptr fork_database::push_block(const signed_block& b) { wlog( "Pushing block to fork database that failed to link: ${id}, ${num}", ("id",b.id())("num",b.block_num()) ); wlog( "Head: ${num}, ${id}", ("num",_head->data.block_num())("id",_head->data.id()) ); - throw; _unlinked_index.insert( item ); + throw; } return _head; } @@ -69,6 +69,8 @@ void fork_database::_push_block(const item_ptr& item) _index.insert(item); if( !_head || item->num > _head->num ) _head = item; + + _push_next( item ); //check for any unlinked blocks that can now be linked to our fork } /** @@ -86,7 +88,15 @@ void fork_database::_push_next( const item_ptr& new_item ) { auto tmp = *itr; prev_idx.erase( itr ); - _push_block( tmp ); + try + { + _push_block( tmp ); + } + catch(const fc::assert_exception& e) + { + //swallow invalid block exception so we can process other unlinked blocks + wdump((e.to_detail_string())); + } itr = prev_idx.find( new_item->id ); } @@ -271,7 +281,7 @@ vector fork_database::fetch_block_range_on_main_branch_by_number( con void fork_database::set_head(shared_ptr h) { - _head = h; + _head = std::move( h ); } void fork_database::remove(block_id_type id) diff --git a/libraries/chain/hive_evaluator.cpp b/libraries/chain/hive_evaluator.cpp index e9e1b49214..f151938060 100644 --- a/libraries/chain/hive_evaluator.cpp +++ b/libraries/chain/hive_evaluator.cpp @@ -13,33 +13,6 @@ #include -#ifndef IS_LOW_MEM -FC_TODO( "After we vendor fc, also vendor diff_match_patch and fix these warnings" ) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wsign-compare" -#pragma GCC diagnostic push -#if !defined( __clang__ ) -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif -#include -#pragma GCC diagnostic pop -#pragma GCC diagnostic pop -#include - -using boost::locale::conv::utf_to_utf; - -std::wstring utf8_to_wstring(const std::string& str) -{ - return utf_to_utf(str.c_str(), str.c_str() + str.size()); -} - -std::string wstring_to_utf8(const std::wstring& str) -{ - return utf_to_utf(str.c_str(), str.c_str() + str.size()); -} - -#endif - #include #include @@ -92,7 +65,8 @@ void witness_update_evaluator::do_apply( const witness_update_operation& o ) else if( !o.props.account_creation_fee.symbol.is_canon() ) { // after HF, above check can be moved to validate() if reindex doesn't show this warning - wlog( "Wrong fee symbol in block ${b}", ("b", _db.head_block_num()+1) ); + _db.push_virtual_operation( system_warning_operation( FC_LOG_MESSAGE( warn, + "Wrong fee symbol in block ${b}", ( "b", _db.head_block_num() + 1 ) ).get_message() ) ); } FC_TODO( "Check and move this to validate after HF 20" ); @@ -379,10 +353,16 @@ void account_create_evaluator::do_apply( const account_create_operation& o ) auth.last_owner_update = fc::time_point_sec::min(); }); + asset initial_vesting_shares; if( !_db.has_hardfork( HIVE_HARDFORK_0_20__1762 ) && o.fee.amount > 0 ) { - _db.create_vesting( new_account, o.fee ); + initial_vesting_shares = _db.create_vesting( new_account, o.fee ); } + else + { + initial_vesting_shares = asset(0, VESTS_SYMBOL); + } + _db.push_virtual_operation( account_created_operation(o.new_account_name, o.creator, initial_vesting_shares, asset(0, VESTS_SYMBOL) ) ); } void account_create_with_delegation_evaluator::do_apply( const account_create_with_delegation_operation& o ) @@ -488,10 +468,16 @@ void account_create_with_delegation_evaluator::do_apply( const account_create_wi }); } + asset initial_vesting_shares; if( !_db.has_hardfork( HIVE_HARDFORK_0_20__1762 ) && o.fee.amount > 0 ) { - _db.create_vesting( new_account, o.fee ); + initial_vesting_shares = _db.create_vesting( new_account, o.fee ); } + else + { + initial_vesting_shares = asset(0, VESTS_SYMBOL); + } + _db.push_virtual_operation( account_created_operation( o.new_account_name, o.creator, initial_vesting_shares, o.delegation) ); } @@ -667,28 +653,18 @@ void delete_comment_evaluator::do_apply( const delete_comment_operation& o ) _db.remove(cur_vote); } - /// this loop can be skiped for validate-only nodes as it is merely gathering stats for indicies + /// this loop can be skiped for validate-only nodes as it is merely gathering stats for indices if( _db.has_hardfork( HIVE_HARDFORK_0_6__80 ) && !comment.is_root() ) { - auto parent = &_db.get_comment( comment.get_parent_id() ); + const comment_cashout_object* parent = _db.find_comment_cashout( _db.get_comment( comment.get_parent_id() ) ); auto now = _db.head_block_time(); - while( parent ) + if( parent ) { - const comment_cashout_object* _parent = _db.find_comment_cashout( *parent ); - if( _parent ) + _db.modify( *parent, [&]( comment_cashout_object& p ) { - _db.modify( *_parent, [&]( comment_cashout_object& p ) - { - p.children--; - p.active = now; - } ); - } - #ifndef IS_LOW_MEM - if( !parent->is_root() ) - parent = &_db.get_comment( parent->get_parent_id() ); - else - #endif - parent = nullptr; + p.children--; + p.active = now; + } ); } } @@ -884,23 +860,7 @@ void comment_evaluator::do_apply( const comment_operation& o ) _db.create< comment_cashout_object >( new_comment, auth, o.permlink, _now, cashout_time, reward_weight ); - #if !defined(IS_LOW_MEM) && defined(STORE_COMMENT_CONTENT) - _db.create< comment_content_object >( [&]( comment_content_object& con ) - { - con.comment = new_comment.get_id(); - - from_string( con.title, o.title ); - if( o.body.size() < 1024*1024*128 ) - { - from_string( con.body, o.body ); - } - from_string( con.json_metadata, o.json_metadata ); - }); - #endif - - -/// this loop can be skiped for validate-only nodes as it is merely gathering stats for indicies - while( parent ) + if( parent ) { const comment_cashout_object* _parent = _db.find_comment_cashout( *parent ); if( _parent ) @@ -910,12 +870,6 @@ void comment_evaluator::do_apply( const comment_operation& o ) p.active = _now; }); } -#ifndef IS_LOW_MEM - if( !parent->is_root() ) - parent = &_db.get_comment( parent->get_parent_id() ); - else -#endif - parent = nullptr; } } @@ -964,37 +918,6 @@ void comment_evaluator::do_apply( const comment_operation& o ) { a.last_post_edit = _now; }); - #if !defined(IS_LOW_MEM) && defined(STORE_COMMENT_CONTENT) - _db.modify( _db.get< comment_content_object, by_comment >( comment.get_id() ), [&]( comment_content_object& con ) - { - if( o.title.size() ) - from_string( con.title, o.title ); - if( o.json_metadata.size() ) - from_string( con.json_metadata, o.json_metadata ); - - if( o.body.size() ) { - try { - diff_match_patch dmp; - auto patch = dmp.patch_fromText( utf8_to_wstring(o.body) ); - if( patch.size() ) { - auto result = dmp.patch_apply( patch, utf8_to_wstring( to_string( con.body ) ) ); - auto patched_body = wstring_to_utf8(result.first); - if( !fc::is_utf8( patched_body ) ) { - idump(("invalid utf8")(patched_body)); - from_string( con.body, fc::prune_invalid_utf8(patched_body) ); - } else { from_string( con.body, patched_body ); } - } - else { // replace - from_string( con.body, o.body ); - } - } catch ( ... ) { - from_string( con.body, o.body ); - } - } - }); - #endif - - } // end EDIT case @@ -1215,6 +1138,8 @@ void transfer_to_vesting_evaluator::do_apply( const transfer_to_vesting_operatio _db.adjust_balance( from_account, -o.amount ); + asset amount_vested; + /* Voting immediately when `transfer_to_vesting` is executed is dangerous, because malicious accounts would take over whole HIVE network. @@ -1223,15 +1148,18 @@ void transfer_to_vesting_evaluator::do_apply( const transfer_to_vesting_operatio */ if( _db.has_hardfork( HIVE_HARDFORK_1_24 ) ) { - asset new_vesting = _db.adjust_account_vesting_balance( to_account, o.amount, false/*to_reward_balance*/, []( asset vests_created ) {} ); + amount_vested = _db.adjust_account_vesting_balance( to_account, o.amount, false/*to_reward_balance*/, []( asset vests_created ) {} ); delayed_voting dv( _db ); - dv.add_delayed_value( to_account, _db.head_block_time(), new_vesting.amount.value ); + dv.add_delayed_value( to_account, _db.head_block_time(), amount_vested.amount.value ); } else { - _db.create_vesting( to_account, o.amount ); + amount_vested = _db.create_vesting( to_account, o.amount ); } + + /// Emit this vop unconditionally, since VESTS balance changed immediately, indepdenent to subsequent updates of account voting power done inside `delayed_voting` mechanism. + _db.push_virtual_operation(transfer_to_vesting_completed_operation(from_account.name, to_account.name, o.amount, amount_vested)); } void withdraw_vesting_evaluator::do_apply( const withdraw_vesting_operation& o ) @@ -1383,27 +1311,22 @@ void set_withdraw_vesting_route_evaluator::do_apply( const set_withdraw_vesting_ void account_witness_proxy_evaluator::do_apply( const account_witness_proxy_operation& o ) { const auto& account = _db.get_account( o.account ); - FC_ASSERT( account.proxy != o.proxy, "Proxy must change." ); - FC_ASSERT( account.can_vote, "Account has declined the ability to vote and cannot proxy votes." ); + _db.modify( account, [&]( account_object& a) { a.update_governance_vote_expiration_ts(_db.head_block_time()); }); - /// remove all current votes - std::array delta; - delta[0] = -account.get_real_vesting_shares(); - for( int i = 0; i < HIVE_MAX_PROXY_RECURSION_DEPTH; ++i ) - delta[i+1] = -account.proxied_vsf_votes[i]; - _db.adjust_proxied_witness_votes( account, delta ); + _db.nullify_proxied_witness_votes( account ); - if( o.proxy.size() ) { + if( !o.is_clearing_proxy() ) { const auto& new_proxy = _db.get_account( o.proxy ); + FC_ASSERT( account.get_proxy() != new_proxy.get_id(), "Proxy must change." ); flat_set proxy_chain( { account.get_id(), new_proxy.get_id() } ); proxy_chain.reserve( HIVE_MAX_PROXY_RECURSION_DEPTH + 1 ); /// check for proxy loops and fail to update the proxy if it would create a loop auto cprox = &new_proxy; - while( cprox->proxy.size() != 0 ) + while( cprox->has_proxy() ) { - const auto& next_proxy = _db.get_account( cprox->proxy ); + const auto& next_proxy = _db.get_account( cprox->get_proxy() ); FC_ASSERT( proxy_chain.insert( next_proxy.get_id() ).second, "This proxy would create a proxy loop." ); cprox = &next_proxy; FC_ASSERT( proxy_chain.size() <= HIVE_MAX_PROXY_RECURSION_DEPTH, "Proxy chain is too long." ); @@ -1413,16 +1336,19 @@ void account_witness_proxy_evaluator::do_apply( const account_witness_proxy_oper _db.clear_witness_votes( account ); _db.modify( account, [&]( account_object& a ) { - a.proxy = o.proxy; + a.set_proxy( new_proxy ); }); /// add all new votes - for( int i = 0; i <= HIVE_MAX_PROXY_RECURSION_DEPTH; ++i ) - delta[i] = -delta[i]; + std::array delta; + delta[0] = account.get_real_vesting_shares(); + for( int i = 0; i < HIVE_MAX_PROXY_RECURSION_DEPTH; ++i ) + delta[i+1] = account.proxied_vsf_votes[i]; _db.adjust_proxied_witness_votes( account, delta ); } else { /// we are clearing the proxy which means we simply update the account + FC_ASSERT( account.has_proxy(), "Proxy must change." ); _db.modify( account, [&]( account_object& a ) { - a.proxy = o.proxy; + a.clear_proxy(); }); } } @@ -1431,10 +1357,9 @@ void account_witness_proxy_evaluator::do_apply( const account_witness_proxy_oper void account_witness_vote_evaluator::do_apply( const account_witness_vote_operation& o ) { const auto& voter = _db.get_account( o.account ); - FC_ASSERT( voter.proxy.size() == 0, "A proxy is currently set, please clear the proxy before voting for a witness." ); - - if( o.approve ) - FC_ASSERT( voter.can_vote, "Account has declined its voting rights." ); + FC_ASSERT( !voter.has_proxy(), "A proxy is currently set, please clear the proxy before voting for a witness." ); + FC_ASSERT( voter.can_vote, "Account has declined its voting rights." ); + _db.modify( voter, [&]( account_object& a) { a.update_governance_vote_expiration_ts(_db.head_block_time()); }); const auto& witness = _db.get_witness( o.witness ); @@ -1511,26 +1436,6 @@ void pre_hf20_vote_evaluator( const vote_operation& o, database& _db ) if( !comment_cashout || ( _db.has_hardfork( HIVE_HARDFORK_0_12__177 ) && _db.calculate_discussion_payout_time( *comment_cashout ) == fc::time_point_sec::maximum() ) ) { -#ifndef CLEAR_VOTES - const auto& comment_vote_idx = _db.get_index< comment_vote_index >().indices().get< by_comment_voter >(); - auto itr = comment_vote_idx.find( boost::make_tuple( comment.get_id(), voter.get_id() ) ); - - if( itr == comment_vote_idx.end() ) - _db.create< comment_vote_object >( [&]( comment_vote_object& cvo ) - { - cvo.voter = voter.get_id(); - cvo.comment = comment.get_id(); - cvo.vote_percent = o.weight; - cvo.last_update = _db.head_block_time(); - }); - else - _db.modify( *itr, [&]( comment_vote_object& cvo ) - { - cvo.vote_percent = o.weight; - cvo.last_update = _db.head_block_time(); - }); -#endif - return; } @@ -1930,37 +1835,17 @@ void hf20_vote_evaluator( const vote_operation& o, database& _db ) } else { - FC_ASSERT( !_db.has_hardfork( HIVE_HARDFORK_1_24 ), "Votes evaluating for comment that is paid out is forbidden." ); + /// Remove this assertion after HF25 + FC_ASSERT((!_db.has_hardfork( HIVE_HARDFORK_1_24 ) || _db.has_hardfork( HIVE_HARDFORK_1_25 )), "Votes evaluating for comment that is paid out is forbidden." ); } if( !comment_cashout || _db.calculate_discussion_payout_time( *comment_cashout ) == fc::time_point_sec::maximum() ) { -#ifndef CLEAR_VOTES - const auto& comment_vote_idx = _db.get_index< comment_vote_index >().indices().get< by_comment_voter >(); - auto itr = comment_vote_idx.find( boost::make_tuple( comment.get_id(), voter.get_id() ) ); - - if( itr == comment_vote_idx.end() ) - _db.create< comment_vote_object >( [&]( comment_vote_object& cvo ) - { - cvo.voter = voter.get_id(); - cvo.comment = comment.get_id(); - cvo.vote_percent = o.weight; - cvo.last_update = _db.head_block_time(); - }); - else - _db.modify( *itr, [&]( comment_vote_object& cvo ) - { - cvo.vote_percent = o.weight; - cvo.last_update = _db.head_block_time(); - }); -#endif - return; } - else - { - FC_ASSERT( _db.head_block_time() < comment_cashout->cashout_time, "Comment is actively being rewarded. Cannot vote on comment." ); - } + + FC_ASSERT( _db.head_block_time() < comment_cashout->cashout_time, "Comment is actively being rewarded. Cannot vote on comment." ); + const auto& comment_vote_idx = _db.get_index< comment_vote_index, by_comment_voter >(); auto itr = comment_vote_idx.find( boost::make_tuple( comment.get_id(), voter.get_id() ) ); @@ -2162,17 +2047,40 @@ void hf20_vote_evaluator( const vote_operation& o, database& _db ) } else { + uint64_t _seconds = (cv.last_update - comment_cashout->get_creation_time()).to_seconds(); + cv.weight = new_weight - old_weight; - max_vote_weight = cv.weight; + //In HF25 `dgpo.reverse_auction_seconds` is set to zero. It's replaced by `dgpo.early_voting_seconds` and `dgpo.mid_voting_seconds`. + if( _seconds < dgpo.reverse_auction_seconds ) + { + max_vote_weight = cv.weight; - /// discount weight by time - uint128_t w(max_vote_weight); - uint64_t delta_t = std::min( uint64_t((cv.last_update - comment_cashout->get_creation_time()).to_seconds()), uint64_t( dgpo.reverse_auction_seconds ) ); + /// discount weight by time + uint128_t w(max_vote_weight); + uint64_t delta_t = std::min( _seconds, uint64_t( dgpo.reverse_auction_seconds ) ); - w *= delta_t; - w /= dgpo.reverse_auction_seconds; - cv.weight = w.to_uint64(); + w *= delta_t; + w /= dgpo.reverse_auction_seconds; + cv.weight = w.to_uint64(); + } + else if( _seconds >= dgpo.early_voting_seconds && dgpo.early_voting_seconds ) + { + //Following values are chosen empirically + const uint32_t phase_1_factor = 2; + const uint32_t phase_2_factor = 8; + + if( _seconds < ( dgpo.early_voting_seconds + dgpo.mid_voting_seconds ) ) + cv.weight /= phase_1_factor; + else + cv.weight /= phase_2_factor; + + max_vote_weight = cv.weight; + } + else + { + max_vote_weight = cv.weight; + } } } else @@ -2488,10 +2396,15 @@ void pow_apply( database& db, Operation o ) /// pay the witness that includes this POW const auto& inc_witness = db.get_account( dgp.current_witness ); + asset actual_reward; if( db.head_block_num() < HIVE_START_MINER_VOTING_BLOCK ) + { db.adjust_balance( inc_witness, pow_reward ); + actual_reward = pow_reward; + } else - db.create_vesting( inc_witness, pow_reward ); + actual_reward = db.create_vesting( inc_witness, pow_reward ); + db.push_virtual_operation( pow_reward_operation( dgp.current_witness, actual_reward ) ); } void pow_evaluator::do_apply( const pow_operation& o ) { @@ -2514,7 +2427,7 @@ void pow2_evaluator::do_apply( const pow2_operation& o ) const auto& work = o.work.get< equihash_pow >(); FC_ASSERT( work.prev_block == db.head_block_id(), "Equihash pow op not for last block" ); auto recent_block_num = protocol::block_header::num_from_id( work.input.prev_block ); - FC_ASSERT( recent_block_num > dgp.last_irreversible_block_num, + FC_ASSERT( recent_block_num > db.get_last_irreversible_block_num(), "Equihash pow done for block older than last irreversible block num" ); FC_ASSERT( work.pow_summary < target_pow, "Insufficient work difficulty. Work: ${w}, Target: ${t}", ("w",work.pow_summary)("t", target_pow) ); worker_account = work.input.worker_account; @@ -2588,7 +2501,8 @@ void pow2_evaluator::do_apply( const pow2_operation& o ) db.adjust_supply( inc_reward, true ); const auto& inc_witness = db.get_account( dgp.current_witness ); - db.create_vesting( inc_witness, inc_reward ); + asset actual_reward = db.create_vesting( inc_witness, inc_reward ); + db.push_virtual_operation( pow_reward_operation( dgp.current_witness, actual_reward ) ); } } @@ -2608,7 +2522,8 @@ void feed_publish_evaluator::do_apply( const feed_publish_operation& o ) void convert_evaluator::do_apply( const convert_operation& o ) { - _db.adjust_balance( o.owner, -o.amount ); + const auto& owner = _db.get_account( o.owner ); + _db.adjust_balance( owner, -o.amount ); const auto& fhistory = _db.get_feed_history(); FC_ASSERT( !fhistory.current_median_history.is_null(), "Cannot convert HBD because there is no price feed." ); @@ -2617,7 +2532,52 @@ void convert_evaluator::do_apply( const convert_operation& o ) if( _db.has_hardfork( HIVE_HARDFORK_0_16__551) ) hive_conversion_delay = HIVE_CONVERSION_DELAY; - _db.create( o.owner, o.amount, _db.head_block_time() + hive_conversion_delay, o.requestid ); + _db.create( owner, o.amount, _db.head_block_time() + hive_conversion_delay, o.requestid ); +} + +void collateralized_convert_evaluator::do_apply( const collateralized_convert_operation& o ) +{ + FC_ASSERT( _db.has_hardfork( HIVE_HARDFORK_1_25 ), "Operation not available until HF25" ); + + const auto& owner = _db.get_account( o.owner ); + _db.adjust_balance( owner, -o.amount ); + + const auto& fhistory = _db.get_feed_history(); + FC_ASSERT( !fhistory.current_median_history.is_null(), "Cannot convert HIVE because there is no price feed." ); + const auto& dgpo = _db.get_dynamic_global_properties(); + FC_ASSERT( dgpo.hbd_print_rate > 0, "Creation of new HBD blocked at this time due to global limit." ); + //note that feed and therefore print rate is updated once per hour, so without above check there could be + //enough room for new HBD, but there is a chance the official price would still be artificial (artificial + //price is not used in this conversion, but users might think it is - better to stop them from making mistake) + + //if you change something here take a look at wallet_api::estimate_hive_collateral as well + + //cut amount by collateral ratio + uint128_t _amount = ( uint128_t( o.amount.amount.value ) * HIVE_100_PERCENT ) / HIVE_CONVERSION_COLLATERAL_RATIO; + asset for_immediate_conversion = asset( _amount.to_uint64(), o.amount.symbol ); + + //immediately create HBD - apply fee to current rolling minimum price + auto converted_amount = multiply_with_fee( for_immediate_conversion, fhistory.current_min_history, + HIVE_COLLATERALIZED_CONVERSION_FEE, HIVE_SYMBOL ); + FC_ASSERT( converted_amount.amount > 0, "Amount of collateral too low - conversion gives no HBD" ); + _db.adjust_balance( owner, converted_amount ); + + _db.modify( dgpo, [&]( dynamic_global_property_object& p ) + { + //HIVE supply (and virtual supply in part related to HIVE) will be corrected after actual conversion + p.current_hbd_supply += converted_amount; + p.virtual_supply += converted_amount * fhistory.current_median_history; + } ); + //note that we created new HBD out of thin air and we will burn the related HIVE when actual conversion takes + //place; there is alternative approach - we could burn all collateral now and print back excess when we are + //to return it to the user; the difference slightly changes calculations below, that is, currently we might + //allow slightly more HBD to be printed + + uint16_t percent_hbd = _db.calculate_HBD_percent(); + FC_ASSERT( percent_hbd <= dgpo.hbd_stop_percent, "Creation of new ${hbd} violates global limit.", ( "hbd", converted_amount ) ); + + _db.create( owner, o.amount, converted_amount, + _db.head_block_time() + HIVE_COLLATERALIZED_CONVERSION_DELAY, o.requestid ); } void limit_order_create_evaluator::do_apply( const limit_order_create_operation& o ) @@ -2778,16 +2738,16 @@ void request_account_recovery_evaluator::do_apply( const request_account_recover { const auto& account_to_recover = _db.get_account( o.account_to_recover ); - if ( account_to_recover.recovery_account.length() ) // Make sure recovery matches expected recovery account + if ( account_to_recover.has_recovery_account() ) // Make sure recovery matches expected recovery account { - FC_ASSERT( account_to_recover.recovery_account == o.recovery_account, "Cannot recover an account that does not have you as their recovery partner." ); + FC_ASSERT( account_to_recover.get_recovery_account() == o.recovery_account, "Cannot recover an account that does not have you as their recovery partner." ); if( o.recovery_account == HIVE_TEMP_ACCOUNT ) wlog( "Recovery by temp account" ); } else // Empty string recovery account defaults to top witness - FC_ASSERT( _db.get_index< witness_index >().indices().get< by_vote_name >().begin()->owner == o.recovery_account, "Top witness must recover an account with no recovery partner." ); + FC_ASSERT( (_db.get_index< witness_index, by_vote_name >().begin()->owner == o.recovery_account), "Top witness must recover an account with no recovery partner." ); - const auto& recovery_request_idx = _db.get_index< account_recovery_request_index >().indices().get< by_account >(); + const auto& recovery_request_idx = _db.get_index< account_recovery_request_index, by_account >(); auto request = recovery_request_idx.find( o.account_to_recover ); if( request == recovery_request_idx.end() ) // New Request @@ -2795,7 +2755,7 @@ void request_account_recovery_evaluator::do_apply( const request_account_recover FC_ASSERT( !o.new_owner_authority.is_impossible(), "Cannot recover using an impossible authority." ); FC_ASSERT( o.new_owner_authority.weight_threshold, "Cannot recover using an open authority." ); - if( _db.is_producing() || _db.has_hardfork( HIVE_HARDFORK_0_20 ) ) + if( _db.has_hardfork( HIVE_HARDFORK_0_20 ) ) { validate_auth_size( o.new_owner_authority ); } @@ -2809,7 +2769,7 @@ void request_account_recovery_evaluator::do_apply( const request_account_recover } } - _db.create< account_recovery_request_object >( o.account_to_recover, o.new_owner_authority, + _db.create< account_recovery_request_object >( account_to_recover, o.new_owner_authority, _db.head_block_time() + HIVE_ACCOUNT_RECOVERY_REQUEST_EXPIRATION_PERIOD ); } else if( o.new_owner_authority.weight_threshold == 0 ) // Cancel Request if authority is open @@ -2831,8 +2791,7 @@ void request_account_recovery_evaluator::do_apply( const request_account_recover _db.modify( *request, [&]( account_recovery_request_object& req ) { - req.new_owner_authority = o.new_owner_authority; - req.expires = _db.head_block_time() + HIVE_ACCOUNT_RECOVERY_REQUEST_EXPIRATION_PERIOD; + req.set_new_owner_authority( o.new_owner_authority, _db.head_block_time() + HIVE_ACCOUNT_RECOVERY_REQUEST_EXPIRATION_PERIOD ); }); } } @@ -2844,13 +2803,13 @@ void recover_account_evaluator::do_apply( const recover_account_operation& o ) if( _db.has_hardfork( HIVE_HARDFORK_0_12 ) ) FC_ASSERT( _db.head_block_time() - account.last_account_recovery > HIVE_OWNER_UPDATE_LIMIT, "Owner authority can only be updated once an hour." ); - const auto& recovery_request_idx = _db.get_index< account_recovery_request_index >().indices().get< by_account >(); + const auto& recovery_request_idx = _db.get_index< account_recovery_request_index, by_account >(); auto request = recovery_request_idx.find( o.account_to_recover ); FC_ASSERT( request != recovery_request_idx.end(), "There are no active recovery requests for this account." ); - FC_ASSERT( request->new_owner_authority == o.new_owner_authority, "New owner authority does not match recovery request." ); + FC_ASSERT( request->get_new_owner_authority() == o.new_owner_authority, "New owner authority does not match recovery request." ); - const auto& recent_auth_idx = _db.get_index< owner_authority_history_index >().indices().get< by_account >(); + const auto& recent_auth_idx = _db.get_index< owner_authority_history_index, by_account >(); auto hist = recent_auth_idx.lower_bound( o.account_to_recover ); bool found = false; @@ -2873,27 +2832,21 @@ void recover_account_evaluator::do_apply( const recover_account_operation& o ) void change_recovery_account_evaluator::do_apply( const change_recovery_account_operation& o ) { - _db.get_account( o.new_recovery_account ); // Simply validate account exists + const auto& new_recovery_account = _db.get_account( o.new_recovery_account ); // validate account exists const auto& account_to_recover = _db.get_account( o.account_to_recover ); - const auto& change_recovery_idx = _db.get_index< change_recovery_account_request_index >().indices().get< by_account >(); + const auto& change_recovery_idx = _db.get_index< change_recovery_account_request_index, by_account >(); auto request = change_recovery_idx.find( o.account_to_recover ); if( request == change_recovery_idx.end() ) // New request { - _db.create< change_recovery_account_request_object >( [&]( change_recovery_account_request_object& req ) - { - req.account_to_recover = o.account_to_recover; - req.recovery_account = o.new_recovery_account; - req.effective_on = _db.head_block_time() + HIVE_OWNER_AUTH_RECOVERY_PERIOD; - }); + _db.create< change_recovery_account_request_object >( account_to_recover, new_recovery_account, _db.head_block_time() + HIVE_OWNER_AUTH_RECOVERY_PERIOD ); } - else if( account_to_recover.recovery_account != o.new_recovery_account ) // Change existing request + else if( account_to_recover.get_recovery_account() != o.new_recovery_account ) // Change existing request { _db.modify( *request, [&]( change_recovery_account_request_object& req ) { - req.recovery_account = o.new_recovery_account; - req.effective_on = _db.head_block_time() + HIVE_OWNER_AUTH_RECOVERY_PERIOD; + req.set_recovery_account( new_recovery_account, _db.head_block_time() + HIVE_OWNER_AUTH_RECOVERY_PERIOD ); }); } else // Request exists and changing back to current recovery account @@ -2981,6 +2934,9 @@ void decline_voting_rights_evaluator::do_apply( const decline_voting_rights_oper void reset_account_evaluator::do_apply( const reset_account_operation& op ) { FC_ASSERT( false, "Reset Account Operation is currently disabled." ); + //ABW: see discussion in https://github.com/steemit/steem/issues/240 + //apparently the idea was never put in active use and it does not seem it ever will + //related member of account_object was removed as it was taking space with no purpose /* const auto& acnt = _db.get_account( op.account_to_reset ); auto band = _db.find< account_bandwidth_object, by_account_bandwidth_type >( boost::make_tuple( op.account_to_reset, bandwidth_type::old_forum ) ); @@ -2995,6 +2951,7 @@ void reset_account_evaluator::do_apply( const reset_account_operation& op ) void set_reset_account_evaluator::do_apply( const set_reset_account_operation& op ) { FC_ASSERT( false, "Set Reset Account Operation is currently disabled." ); + //related to reset_account_operation /* const auto& acnt = _db.get_account( op.account ); _db.get_account( op.reset_account ); @@ -3380,4 +3337,51 @@ void delegate_vesting_shares_evaluator::do_apply( const delegate_vesting_shares_ } } + +void recurrent_transfer_evaluator::do_apply( const recurrent_transfer_operation& op ) +{ + FC_ASSERT( _db.has_hardfork( HIVE_HARDFORK_1_25 ), "Recurrent transfers are not enabled until hardfork ${hf}", ("hf", HIVE_HARDFORK_1_25) ); + + const auto& from_account = _db.get_account(op.from ); + const auto& to_account = _db.get_account( op.to ); + + asset available = _db.get_balance( from_account, op.amount.symbol ); + FC_ASSERT( available >= op.amount, "Account does not have enough tokens for the first transfer, has ${has} needs ${needs}", ("has", available)("needs", op.amount) ); + + const auto& rt_idx = _db.get_index< recurrent_transfer_index, by_from_to_id >(); + auto itr = rt_idx.find(boost::make_tuple(from_account.get_id(), to_account.get_id())); + + if( itr == rt_idx.end() ) + { + FC_ASSERT( from_account.open_recurrent_transfers < HIVE_MAX_OPEN_RECURRENT_TRANSFERS, "Account can't have more than ${rt} recurrent transfers", ( "rt", HIVE_MAX_OPEN_RECURRENT_TRANSFERS ) ); + // If the recurrent transfer is not found and the amount is 0 it means the user wants to delete a transfer that doesnt exists + FC_ASSERT( op.amount.amount != 0, "Cannot create a recurrent transfer with 0 amount"); + _db.create< recurrent_transfer_object >(_db.head_block_time(), from_account, to_account, op.amount, op.memo, op.recurrence, op.executions); + + _db.modify(from_account, [](account_object& a ) + { + a.open_recurrent_transfers++; + }); + } + else if( op.amount.amount == 0 ) + { + _db.remove( *itr ); + _db.modify(from_account, [&](account_object& a ) + { + FC_ASSERT( a.open_recurrent_transfers > 0 ); + a.open_recurrent_transfers--; + }); + } + else + { + _db.modify( *itr, [&]( recurrent_transfer_object& rt ) + { + rt.amount = op.amount; + from_string( rt.memo, op.memo ); + rt.set_recurrence_trigger_date(_db.head_block_time(), op.recurrence); + rt.remaining_executions = op.executions; + }); + } +} + } } // hive::chain diff --git a/libraries/chain/include/hive/chain/account_object.hpp b/libraries/chain/include/hive/chain/account_object.hpp index 7520eb055d..c185bcc4fc 100644 --- a/libraries/chain/include/hive/chain/account_object.hpp +++ b/libraries/chain/include/hive/chain/account_object.hpp @@ -12,7 +12,6 @@ #include #include -#include #include @@ -32,8 +31,8 @@ namespace hive { namespace chain { const time_point_sec& _creation_time, bool _mined, const account_name_type& _recovery_account, bool _fill_mana, const asset& incoming_delegation ) - : id( _id ), name( _name ), memo_key( _memo_key ), created( _creation_time ), mined( _mined ), - recovery_account( _recovery_account ), delayed_votes( a ) + : id( _id ), name( _name ), recovery_account( _recovery_account ), created( _creation_time ), + mined( _mined ), memo_key( _memo_key ), delayed_votes( a ) { received_vesting_shares += incoming_delegation; voting_manabar.last_update_time = _creation_time.sec_since_epoch(); @@ -75,28 +74,48 @@ namespace hive { namespace chain { //value of unclaimed VESTS rewards in HIVE (HIVE held on global balance) asset get_vest_rewards_as_hive() const { return reward_vesting_hive; } - account_name_type name; - public_key_type memo_key; - account_name_type proxy; + //gives name of the account + const account_name_type& get_name() const { return name; } + + //tells if account has some other account casting governance votes in its name + bool has_proxy() const { return proxy != account_id_type(); } + //account's proxy (if any) + account_id_type get_proxy() const { return proxy; } + //sets proxy to neutral (account will vote for itself) + void clear_proxy() { proxy = account_id_type(); } + //sets proxy to given account + void set_proxy(const account_object& new_proxy) + { + FC_ASSERT( &new_proxy != this ); + proxy = new_proxy.get_id(); + } - time_point_sec last_account_update; + //tells if account has some designated account that can initiate recovery (if not, top witness can) + bool has_recovery_account() const { return recovery_account.length(); } + //account's recovery account (if any), that is, an account that can authorize request_account_recovery_operation + const account_name_type& get_recovery_account() const { return recovery_account; } + //sets new recovery account + void set_recovery_account(const account_object& new_recovery_account) + { + recovery_account = new_recovery_account.name; + } - time_point_sec created; - bool mined = true; - account_name_type recovery_account; - account_name_type reset_account = HIVE_NULL_ACCOUNT; - time_point_sec last_account_recovery; - uint32_t comment_count = 0; - uint32_t lifetime_vote_count = 0; - uint32_t post_count = 0; + //members are organized in such a way that the object takes up as little space as possible (note that object starts with 4byte id). + + private: + account_id_type proxy; + public: + account_name_type name; + private: + account_name_type recovery_account; //cannot be changed to id because there are plenty of accounts with "steem" recovery created before it was created in b.1097 + + public: + uint128_t hbd_seconds; ///< total HBD * how long it has been held + uint128_t savings_hbd_seconds; ///< total HBD * how long it has been held - bool can_vote = true; util::manabar voting_manabar; util::manabar downvote_manabar; - HIVE_asset balance = asset( 0, HIVE_SYMBOL ); ///< total liquid shares held by this account - HIVE_asset savings_balance = asset( 0, HIVE_SYMBOL ); ///< total liquid shares held by this account - /** * HBD Deposits pay interest based upon the interest rate set by witnesses. The purpose of these * fields is to track the total (time * hbd_balance) that it is held. Then at the appointed time @@ -110,51 +129,71 @@ namespace hive { namespace chain { * * @defgroup hbd_data HBD Balance Data */ - ///@{ - HBD_asset hbd_balance = asset( 0, HBD_SYMBOL ); /// total HBD balance - uint128_t hbd_seconds; ///< total HBD * how long it has been held - time_point_sec hbd_seconds_last_update; ///< the last time the hbd_seconds was updated - time_point_sec hbd_last_interest_payment; ///< used to pay interest at most once per month - + HBD_asset hbd_balance = asset( 0, HBD_SYMBOL ); /// total HBD balance HBD_asset savings_hbd_balance = asset( 0, HBD_SYMBOL ); /// total HBD balance - uint128_t savings_hbd_seconds; ///< total HBD * how long it has been held - time_point_sec savings_hbd_seconds_last_update; ///< the last time the hbd_seconds was updated - time_point_sec savings_hbd_last_interest_payment; ///< used to pay interest at most once per month - - uint8_t savings_withdraw_requests = 0; - ///@} - HBD_asset reward_hbd_balance = asset( 0, HBD_SYMBOL ); + HIVE_asset reward_hive_balance = asset( 0, HIVE_SYMBOL ); - VEST_asset reward_vesting_balance = asset( 0, VESTS_SYMBOL ); HIVE_asset reward_vesting_hive = asset( 0, HIVE_SYMBOL ); + HIVE_asset balance = asset( 0, HIVE_SYMBOL ); ///< total liquid shares held by this account + HIVE_asset savings_balance = asset( 0, HIVE_SYMBOL ); ///< total liquid shares held by this account - share_type curation_rewards = 0; - share_type posting_rewards = 0; - + VEST_asset reward_vesting_balance = asset( 0, VESTS_SYMBOL ); VEST_asset vesting_shares = asset( 0, VESTS_SYMBOL ); ///< total vesting shares held by this account, controls its voting power VEST_asset delegated_vesting_shares = asset( 0, VESTS_SYMBOL ); VEST_asset received_vesting_shares = asset( 0, VESTS_SYMBOL ); - VEST_asset vesting_withdraw_rate = asset( 0, VESTS_SYMBOL ); ///< at the time this is updated it can be at most vesting_shares/104 - time_point_sec next_vesting_withdrawal = fc::time_point_sec::maximum(); ///< after every withdrawal this is incremented by 1 week + + share_type curation_rewards = 0; + share_type posting_rewards = 0; share_type withdrawn = 0; /// Track how many shares have been withdrawn share_type to_withdraw = 0; /// Might be able to look this up with operation history. - uint16_t withdraw_routes = 0; //max 10, why is it 16bit? - uint16_t pending_transfers = 0; //for now max is 255, but it might change - - fc::array proxied_vsf_votes;// = std::vector( HIVE_MAX_PROXY_RECURSION_DEPTH, 0 ); ///< the total VFS votes proxied to this account + share_type pending_claimed_accounts = 0; - uint16_t witnesses_voted_for = 0; //max 30, why is it 16bit? + /* + Total sum of VESTS from `delayed_votes` collection. + It's a helper variable needed for better performance. + */ + ushare_type sum_delayed_votes = 0; + time_point_sec hbd_seconds_last_update; ///< the last time the hbd_seconds was updated + time_point_sec hbd_last_interest_payment; ///< used to pay interest at most once per month + time_point_sec savings_hbd_seconds_last_update; ///< the last time the hbd_seconds was updated + time_point_sec savings_hbd_last_interest_payment; ///< used to pay interest at most once per month + time_point_sec last_account_recovery; + time_point_sec created; + time_point_sec last_account_update; time_point_sec last_post; time_point_sec last_root_post = fc::time_point_sec::min(); time_point_sec last_post_edit; time_point_sec last_vote_time; + time_point_sec next_vesting_withdrawal = fc::time_point_sec::maximum(); ///< after every withdrawal this is incremented by 1 week + + private: + time_point_sec governance_vote_expiration_ts = fc::time_point_sec::maximum(); + + public: + + uint32_t comment_count = 0; + uint32_t lifetime_vote_count = 0; + uint32_t post_count = 0; uint32_t post_bandwidth = 0; - share_type pending_claimed_accounts = 0; + uint16_t withdraw_routes = 0; //max 10, why is it 16bit? + uint16_t pending_transfers = 0; //for now max is 255, but it might change + uint16_t open_recurrent_transfers = 0; //for now max is 255, but it might change + uint16_t witnesses_voted_for = 0; //max 30, why is it 16bit? + + uint8_t savings_withdraw_requests = 0; + bool can_vote = true; + bool mined = true; + + public: + + public_key_type memo_key; //public_key_type - 33 bytes; ABW: it belongs to metadata as it is not used by consensus, but witnesses need it here since they don't COLLECT_ACCOUNT_METADATA + + fc::array proxied_vsf_votes;// = std::vector( HIVE_MAX_PROXY_RECURSION_DEPTH, 0 ); ///< the total VFS votes proxied to this account using t_delayed_votes = t_vector< delayed_votes_data >; /* @@ -162,11 +201,28 @@ namespace hive { namespace chain { VESTS from day `X` will be matured after `X` + 30 days ( because `HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS` == 30 days ) */ t_delayed_votes delayed_votes; - /* - Total sum of VESTS from `delayed_votes` collection. - It's a helper variable needed for better performance. - */ - ushare_type sum_delayed_votes = 0; + + //methods + + time_point_sec get_governance_vote_expiration_ts() const + { + return governance_vote_expiration_ts; + } + + void set_governance_vote_expired() + { + governance_vote_expiration_ts = time_point_sec::maximum(); + } + + void update_governance_vote_expiration_ts(const time_point_sec vote_time) + { + governance_vote_expiration_ts = vote_time + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD; + if (governance_vote_expiration_ts < HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP) + { + const int64_t DIVIDER = HIVE_HARDFORK_1_25_MAX_OLD_GOVERNANCE_VOTE_EXPIRE_SHIFT.to_seconds(); + governance_vote_expiration_ts = HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP + fc::seconds(governance_vote_expiration_ts.sec_since_epoch() % DIVIDER); + } + } time_point_sec get_the_earliest_time() const { @@ -290,16 +346,33 @@ namespace hive { namespace chain { public: template< typename Allocator > account_recovery_request_object( allocator< Allocator > a, uint64_t _id, - const account_name_type& _account_to_recover, const authority& _new_owner_authority, const time_point_sec& _expiration_time ) - : id( _id ), account_to_recover( _account_to_recover ), new_owner_authority( allocator< shared_authority >( a ) ), - expires( _expiration_time ) + const account_object& _account_to_recover, const authority& _new_owner_authority, const time_point_sec& _expiration_time ) + : id( _id ), expires( _expiration_time ), account_to_recover( _account_to_recover.get_name() ), + new_owner_authority( allocator< shared_authority >( a ) ) { new_owner_authority = _new_owner_authority; } + //account whos owner authority is being modified + const account_name_type& get_account_to_recover() const { return account_to_recover; } + + //new owner authority requested to be set during recovery operation + const shared_authority& get_new_owner_authority() const { return new_owner_authority; } + //sets different new owner authority (also moves time when request will expire) + void set_new_owner_authority( const authority& _new_owner_authority, const time_point_sec& _new_expiration_time ) + { + new_owner_authority = _new_owner_authority; + expires = _new_expiration_time; + } + + //time when the request will be automatically removed if not used + time_point_sec get_expiration_time() const { return expires; } + + private: + time_point_sec expires; account_name_type account_to_recover; shared_authority new_owner_authority; - time_point_sec expires; + CHAINBASE_UNPACK_CONSTRUCTOR(account_recovery_request_object, (new_owner_authority)); }; @@ -307,17 +380,39 @@ namespace hive { namespace chain { { CHAINBASE_OBJECT( change_recovery_account_request_object ); public: - CHAINBASE_DEFAULT_CONSTRUCTOR( change_recovery_account_request_object ) + template< typename Allocator > + change_recovery_account_request_object( allocator< Allocator > a, uint64_t _id, + const account_object& _account_to_recover, const account_object& _recovery_account, const time_point_sec& _effect_time ) + : id( _id ), effective_on( _effect_time ), account_to_recover( _account_to_recover.name ), recovery_account( _recovery_account.name ) + {} - account_name_type account_to_recover; - account_name_type recovery_account; + //account whos recovery account is being modified + const account_name_type& get_account_to_recover() const { return account_to_recover; } + + //new recovery account being set + const account_name_type& get_recovery_account() const { return recovery_account; } + //sets different new recovery account (also moves time when actual change will take place) + void set_recovery_account( const account_object& new_recovery_account, const time_point_sec& _new_effect_time ) + { + recovery_account = new_recovery_account.name; + effective_on = _new_effect_time; + } + + //time when actual change of recovery account is to be executed + time_point_sec get_execution_time() const { return effective_on; } + + private: time_point_sec effective_on; + account_name_type account_to_recover; //changing it to id would influence response from database_api.list_change_recovery_account_requests + account_name_type recovery_account; //could be changed to id + CHAINBASE_UNPACK_CONSTRUCTOR(change_recovery_account_request_object); }; struct by_proxy; struct by_next_vesting_withdrawal; struct by_delayed_voting; + struct by_governance_vote_expiration_ts; /** * @ingroup object_index */ @@ -330,7 +425,7 @@ namespace hive { namespace chain { member< account_object, account_name_type, &account_object::name > >, ordered_unique< tag< by_proxy >, composite_key< account_object, - member< account_object, account_name_type, &account_object::proxy >, + const_mem_fun< account_object, account_id_type, &account_object::get_proxy >, member< account_object, account_name_type, &account_object::name > > /// composite key by proxy >, @@ -345,6 +440,12 @@ namespace hive { namespace chain { const_mem_fun< account_object, time_point_sec, &account_object::get_the_earliest_time >, const_mem_fun< account_object, account_object::id_type, &account_object::get_id > > + >, + ordered_unique< tag< by_governance_vote_expiration_ts >, + composite_key< account_object, + const_mem_fun< account_object, time_point_sec, &account_object::get_governance_vote_expiration_ts >, + const_mem_fun< account_object, account_object::id_type, &account_object::get_id > + > > >, allocator< account_object > @@ -458,14 +559,14 @@ namespace hive { namespace chain { ordered_unique< tag< by_id >, const_mem_fun< account_recovery_request_object, account_recovery_request_object::id_type, &account_recovery_request_object::get_id > >, ordered_unique< tag< by_account >, - member< account_recovery_request_object, account_name_type, &account_recovery_request_object::account_to_recover > + const_mem_fun< account_recovery_request_object, const account_name_type&, &account_recovery_request_object::get_account_to_recover > >, ordered_unique< tag< by_expiration >, composite_key< account_recovery_request_object, - member< account_recovery_request_object, time_point_sec, &account_recovery_request_object::expires >, - member< account_recovery_request_object, account_name_type, &account_recovery_request_object::account_to_recover > + const_mem_fun< account_recovery_request_object, time_point_sec, &account_recovery_request_object::get_expiration_time >, + const_mem_fun< account_recovery_request_object, const account_name_type&, &account_recovery_request_object::get_account_to_recover > >, - composite_key_compare< std::less< time_point_sec >, std::less< account_name_type > > + composite_key_compare< std::less< time_point_sec >, std::less< const account_name_type& > > > >, allocator< account_recovery_request_object > @@ -479,34 +580,24 @@ namespace hive { namespace chain { ordered_unique< tag< by_id >, const_mem_fun< change_recovery_account_request_object, change_recovery_account_request_object::id_type, &change_recovery_account_request_object::get_id > >, ordered_unique< tag< by_account >, - member< change_recovery_account_request_object, account_name_type, &change_recovery_account_request_object::account_to_recover > + const_mem_fun< change_recovery_account_request_object, const account_name_type&, &change_recovery_account_request_object::get_account_to_recover > >, ordered_unique< tag< by_effective_date >, composite_key< change_recovery_account_request_object, - member< change_recovery_account_request_object, time_point_sec, &change_recovery_account_request_object::effective_on >, - member< change_recovery_account_request_object, account_name_type, &change_recovery_account_request_object::account_to_recover > + const_mem_fun< change_recovery_account_request_object, time_point_sec, &change_recovery_account_request_object::get_execution_time >, + const_mem_fun< change_recovery_account_request_object, const account_name_type&, &change_recovery_account_request_object::get_account_to_recover > >, - composite_key_compare< std::less< time_point_sec >, std::less< account_name_type > > + composite_key_compare< std::less< time_point_sec >, std::less< const account_name_type& > > > >, allocator< change_recovery_account_request_object > > change_recovery_account_request_index; } } -#ifdef ENABLE_MIRA -namespace mira { - -template<> struct is_static_length< hive::chain::vesting_delegation_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::vesting_delegation_expiration_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::change_recovery_account_request_object > : public boost::true_type {}; - -} // mira -#endif - FC_REFLECT( hive::chain::account_object, (id)(name)(memo_key)(proxy)(last_account_update) (created)(mined) - (recovery_account)(last_account_recovery)(reset_account) + (recovery_account)(last_account_recovery) (comment_count)(lifetime_vote_count)(post_count)(can_vote)(voting_manabar)(downvote_manabar) (balance) (savings_balance) @@ -515,13 +606,14 @@ FC_REFLECT( hive::chain::account_object, (reward_hive_balance)(reward_hbd_balance)(reward_vesting_balance)(reward_vesting_hive) (vesting_shares)(delegated_vesting_shares)(received_vesting_shares) (vesting_withdraw_rate)(next_vesting_withdrawal)(withdrawn)(to_withdraw)(withdraw_routes) - (pending_transfers)(curation_rewards) + (pending_transfers)(open_recurrent_transfers)(curation_rewards) (posting_rewards) (proxied_vsf_votes)(witnesses_voted_for) (last_post)(last_root_post)(last_post_edit)(last_vote_time)(post_bandwidth) (pending_claimed_accounts) (delayed_votes) (sum_delayed_votes) + (governance_vote_expiration_ts) ) CHAINBASE_SET_INDEX_TYPE( hive::chain::account_object, hive::chain::account_index ) diff --git a/libraries/chain/include/hive/chain/block_summary_object.hpp b/libraries/chain/include/hive/chain/block_summary_object.hpp index 14e01abbc4..ab3dc3ebf2 100644 --- a/libraries/chain/include/hive/chain/block_summary_object.hpp +++ b/libraries/chain/include/hive/chain/block_summary_object.hpp @@ -34,13 +34,5 @@ namespace hive { namespace chain { } } // hive::chain -#ifdef ENABLE_MIRA -namespace mira { - -template<> struct is_static_length< hive::chain::block_summary_object > : public boost::true_type {}; - -} // mira -#endif - FC_REFLECT( hive::chain::block_summary_object, (id)(block_id) ) CHAINBASE_SET_INDEX_TYPE( hive::chain::block_summary_object, hive::chain::block_summary_index ) diff --git a/libraries/chain/include/hive/chain/buffer_type.hpp b/libraries/chain/include/hive/chain/buffer_type.hpp index 95eeaf8410..cf949f940e 100644 --- a/libraries/chain/include/hive/chain/buffer_type.hpp +++ b/libraries/chain/include/hive/chain/buffer_type.hpp @@ -37,6 +37,6 @@ template< typename T, typename B > inline T unpack_from_buffer( const B& raw ) } } // fc::raw -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR FC_REFLECT_TYPENAME( hive::chain::buffer_type ) -#endif +#endif \ No newline at end of file diff --git a/libraries/chain/include/hive/chain/comment_object.hpp b/libraries/chain/include/hive/chain/comment_object.hpp index e4a13c50d2..01d8498a28 100644 --- a/libraries/chain/include/hive/chain/comment_object.hpp +++ b/libraries/chain/include/hive/chain/comment_object.hpp @@ -8,8 +8,6 @@ #include #include -#include - #include #define HIVE_ROOT_POST_PARENT_ID hive::chain::account_id_type::null_id() @@ -172,21 +170,6 @@ namespace hive { namespace chain { #endif }; - class comment_content_object : public object< comment_content_object_type, comment_content_object > - { - CHAINBASE_OBJECT( comment_content_object ); - public: - CHAINBASE_DEFAULT_CONSTRUCTOR( comment_content_object, (title)(body)(json_metadata) ) - - comment_id_type comment; - - shared_string title; - shared_string body; - shared_string json_metadata; - - CHAINBASE_UNPACK_CONSTRUCTOR(comment_content_object, (title)(body)(json_metadata)); - }; - /** * This index maintains the set of voter/comment pairs that have been used, voters cannot * vote on the same comment more than once per payout period. @@ -274,29 +257,8 @@ namespace hive { namespace chain { allocator< comment_cashout_object > > comment_cashout_index; - struct by_comment; - - typedef multi_index_container< - comment_content_object, - indexed_by< - ordered_unique< tag< by_id >, - const_mem_fun< comment_content_object, comment_content_object::id_type, &comment_content_object::get_id > >, - ordered_unique< tag< by_comment >, - member< comment_content_object, comment_id_type, &comment_content_object::comment > > - >, - allocator< comment_content_object > - > comment_content_index; - } } // hive::chain -#ifdef ENABLE_MIRA -namespace mira { - -template<> struct is_static_length< hive::chain::comment_vote_object > : public boost::true_type {}; - -} // mira -#endif - FC_REFLECT( hive::chain::comment_object, (id)(root_comment)(parent_comment) (author_and_permlink_hash)(depth) @@ -320,10 +282,6 @@ FC_REFLECT( hive::chain::comment_cashout_object, CHAINBASE_SET_INDEX_TYPE( hive::chain::comment_cashout_object, hive::chain::comment_cashout_index ) -FC_REFLECT( hive::chain::comment_content_object, - (id)(comment)(title)(body)(json_metadata) ) -CHAINBASE_SET_INDEX_TYPE( hive::chain::comment_content_object, hive::chain::comment_content_index ) - FC_REFLECT( hive::chain::comment_vote_object, (id)(voter)(comment)(weight)(rshares)(vote_percent)(last_update)(num_changes) ) @@ -376,29 +334,4 @@ namespace helpers } }; - template <> - class index_statistic_provider - { - public: - typedef hive::chain::comment_content_index IndexType; - - index_statistic_info gather_statistics(const IndexType& index, bool onlyStaticInfo) const - { - index_statistic_info info; - gather_index_static_data(index, &info); - - if(onlyStaticInfo == false) - { - for(const auto& o : index) - { - info._item_additional_allocation += o.title.capacity()*sizeof(shared_string::value_type); - info._item_additional_allocation += o.body.capacity()*sizeof(shared_string::value_type); - info._item_additional_allocation += o.json_metadata.capacity()*sizeof(shared_string::value_type); - } - } - - return info; - } - }; - } /// namespace helpers \ No newline at end of file diff --git a/libraries/chain/include/hive/chain/database.hpp b/libraries/chain/include/hive/chain/database.hpp index 032486a7d0..25f1420115 100644 --- a/libraries/chain/include/hive/chain/database.hpp +++ b/libraries/chain/include/hive/chain/database.hpp @@ -47,6 +47,7 @@ namespace chain { struct prepare_snapshot_supplement_notification; struct load_snapshot_supplement_notification; + class database; struct hardfork_versions { @@ -54,20 +55,6 @@ namespace chain { protocol::hardfork_version versions[ HIVE_NUM_HARDFORKS + 1 ]; }; - class database; - -#ifdef ENABLE_MIRA - using set_index_type_func = std::function< void(database&, mira::index_type, const boost::filesystem::path&, const boost::any&) >; -#endif - - struct index_delegate { -#ifdef ENABLE_MIRA - set_index_type_func set_index_type; -#endif - }; - - using index_delegate_map = std::map< std::string, index_delegate >; - class database_impl; class custom_operation_interpreter; @@ -95,7 +82,6 @@ namespace chain { uint64_t shared_file_size = 0; uint16_t shared_file_full_threshold = 0; uint16_t shared_file_scale_rate = 0; - int16_t sps_remove_threshold = -1; uint32_t chainbase_flags = 0; bool do_validate_invariants = false; bool benchmark_is_enabled = false; @@ -165,6 +151,7 @@ namespace chain { private: uint32_t reindex_internal( const open_args& args, signed_block& block ); + void remove_expired_governance_votes(); public: @@ -220,14 +207,20 @@ namespace chain { /// Warning: to correctly process old blocks initially old chain-id should be set. chain_id_type hive_chain_id = STEEM_CHAIN_ID; + /// Returns current chain-id being in use depending on applied HF chain_id_type get_chain_id() const; + /// Returns pre-HF24 chain id (if mainnet is used). + chain_id_type get_old_chain_id() const; + /// Returns post-HF24 chain id (if mainnet is used). + chain_id_type get_new_chain_id() const; + void set_chain_id( const chain_id_type& chain_id ); /** Allows to visit all stored blocks until processor returns true. Caller is responsible for block disasembling * const signed_block_header& - header of previous block * const signed_block& - block to be processed currently */ - void foreach_block(std::function processor) const; + void foreach_block(const std::function& processor) const; /// Allows to process all blocks visit all transactions held there until processor returns true. void foreach_tx(std::function; using prepare_snapshot_data_supplement_handler_t = std::function < void(const prepare_snapshot_supplement_notification&) >; using load_snapshot_data_supplement_handler_t = std::function < void(const load_snapshot_supplement_notification&) >; + using comment_reward_notification_handler_t = std::function < void(const comment_reward_notification&) >; void notify_prepare_snapshot_data_supplement(const prepare_snapshot_supplement_notification& n); void notify_load_snapshot_data_supplement(const load_snapshot_supplement_notification& n); + void notify_comment_reward(const comment_reward_notification& note); private: template boost::signals2::connection add_snapshot_supplement_handler (const load_snapshot_data_supplement_handler_t& func, const abstract_plugin& plugin, int32_t group = -1); + boost::signals2::connection add_comment_reward_handler (const comment_reward_notification_handler_t& func, const abstract_plugin& plugin, int32_t group = -1); + //////////////////// db_witness_schedule.cpp //////////////////// /** @@ -460,7 +457,7 @@ namespace chain { using Before = std::function< void( const asset& ) >; asset adjust_account_vesting_balance(const account_object& to_account, const asset& liquid, bool to_reward_balance, Before&& before_vesting_callback ); - asset create_vesting( const account_object& to_account, asset hive, bool to_reward_balance=false ); + asset create_vesting( const account_object& to_account, const asset& liquid, bool to_reward_balance=false ); void adjust_total_payout( const comment_cashout_object& a, const asset& hbd, const asset& curator_hbd_value, const asset& beneficiary_value ); @@ -498,16 +495,18 @@ namespace chain { /** this updates the votes for all witnesses as a result of account VESTS changing */ void adjust_proxied_witness_votes( const account_object& a, share_type delta, int depth = 0 ); + /** like above with delta that negates all vote power for given user - both for individual witnesses and/or proxy */ + void nullify_proxied_witness_votes( const account_object& a ); /** this is called by `adjust_proxied_witness_votes` when account proxy to self */ - void adjust_witness_votes( const account_object& a, share_type delta ); + void adjust_witness_votes( const account_object& a, const share_type& delta ); /** this updates the vote of a single witness as a result of a vote being added or removed*/ void adjust_witness_vote( const witness_object& obj, share_type delta ); /** clears all vote records for a particular account but does not update the * witness vote totals. Vote totals should be updated first via a call to - * adjust_proxied_witness_votes( a, -a.witness_vote_weight() ) + * nullify_proxied_witness_votes( a ) */ void clear_witness_votes( const account_object& a ); void process_vesting_withdrawals(); @@ -531,7 +530,7 @@ namespace chain { uint16_t get_curation_rewards_percent() const; - share_type pay_reward_funds( share_type reward ); + share_type pay_reward_funds( const share_type& reward ); void pay_liquidity_reward(); @@ -548,7 +547,12 @@ namespace chain { node_property_object& node_properties(); - uint32_t last_non_undoable_block_num() const; + uint32_t get_last_irreversible_block_num()const; + void set_last_irreversible_block_num(uint32_t block_num); + struct irreversible_object_type + { + uint32_t last_irreversible_block_num = 0; + } *irreversible_object = nullptr; //////////////////// db_init.cpp //////////////////// void initialize_evaluators(); @@ -558,6 +562,9 @@ namespace chain { /// Reset the object graph in-memory void initialize_indexes(); + // Reset irreversible state (unaffected by undo) + void initialize_irreversible_storage(); + void resetState(const open_args& args); void init_schema(); @@ -584,6 +591,7 @@ namespace chain { void retally_witness_votes(); void retally_witness_vote_counts( bool force = false ); void retally_liquidity_weight(); + uint16_t calculate_HBD_percent(); void update_virtual_supply(); bool has_hardfork( uint32_t hardfork )const; @@ -610,11 +618,6 @@ namespace chain { optional< chainbase::database::session >& pending_transaction_session(); - void set_index_delegate( const std::string& n, index_delegate&& d ); - const index_delegate& get_index_delegate( const std::string& n ); - bool has_index_delegate( const std::string& n ); - const index_delegate_map& index_delegates(); - #ifdef IS_TEST_NET bool liquidity_rewards_enabled = true; bool skip_price_feed_limit_check = true; @@ -676,10 +679,12 @@ namespace chain { void process_delayed_voting(const block_notification& note ); + void process_recurrent_transfers(); + void update_global_dynamic_data( const signed_block& b ); void update_signing_witness(const witness_object& signing_witness, const signed_block& new_block); - void update_last_irreversible_block(); - void migrate_irreversible_state(); + uint32_t update_last_irreversible_block(); + void migrate_irreversible_state(uint32_t old_last_irreversible); void clear_expired_transactions(); void clear_expired_orders(); void clear_expired_delegations(); @@ -721,14 +726,17 @@ namespace chain { return _current_op_in_trx; } - int16_t get_sps_remove_threshold() const + int16_t get_remove_threshold() const { - return _sps_remove_threshold; + return get_dynamic_global_properties().current_remove_threshold; } - void set_sps_remove_threshold( int16_t val ) + void set_remove_threshold( int16_t val ) { - _sps_remove_threshold = val; + modify( get_dynamic_global_properties(), [&]( dynamic_global_property_object& gpo ) + { + gpo.current_remove_threshold = val; + } ); } bool get_snapshot_loaded() const @@ -783,7 +791,6 @@ namespace chain { uint16_t _shared_file_full_threshold = 0; uint16_t _shared_file_scale_rate = 0; - int16_t _sps_remove_threshold = -1; bool snapshot_loaded = false; @@ -791,7 +798,6 @@ namespace chain { std::string _json_schema; util::advanced_benchmark_dumper _benchmark_dumper; - index_delegate_map _index_delegate_map; fc::signal _pre_apply_required_action_signal; fc::signal _post_apply_required_action_signal; @@ -881,6 +887,11 @@ namespace chain { * Internal signal to execute deferred registration of plugin indexes. */ fc::signal _plugin_index_signal; + + /// + /// Emitted when rewards for author and curators are paid out. + /// + fc::signal _comment_reward_signal; }; struct reindex_notification @@ -908,5 +919,4 @@ namespace chain { hive::plugins::chain::snapshot_load_helper& load_helper; }; - } } diff --git a/libraries/chain/include/hive/chain/evaluator.hpp b/libraries/chain/include/hive/chain/evaluator.hpp index 218f960e87..634321b57e 100644 --- a/libraries/chain/include/hive/chain/evaluator.hpp +++ b/libraries/chain/include/hive/chain/evaluator.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace hive { namespace chain { diff --git a/libraries/chain/include/hive/chain/global_property_object.hpp b/libraries/chain/include/hive/chain/global_property_object.hpp index ce493139e7..452de051f9 100644 --- a/libraries/chain/include/hive/chain/global_property_object.hpp +++ b/libraries/chain/include/hive/chain/global_property_object.hpp @@ -137,8 +137,6 @@ namespace hive { namespace chain { fc::uint128_t recent_slots_filled = fc::uint128::max_value(); uint8_t participation_count = 128; ///< Divide by 128 to compute participation percentage - uint32_t last_irreversible_block_num = 0; - /** * The number of votes regenerated per day. Any user voting slower than this rate will be * "wasting" voting power through spillover; any user voting faster than this rate will have @@ -149,6 +147,8 @@ namespace hive { namespace chain { uint32_t delegation_return_period = HIVE_DELEGATION_RETURN_PERIOD_HF0; uint64_t reverse_auction_seconds = HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF6; + uint64_t early_voting_seconds = 0; + uint64_t mid_voting_seconds = 0; int64_t available_account_subsidies = 0; @@ -169,6 +169,15 @@ namespace hive { namespace chain { uint16_t downvote_pool_percent = 0; + // limits number of objects removed in one automatic operation (only applies to situations where many + // objects can accumulate over time but need to be removed in single operation f.e. proposal votes) + int16_t current_remove_threshold = HIVE_GLOBAL_REMOVE_THRESHOLD; //negative means no limit + + uint8_t max_consecutive_recurrent_transfer_failures = HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES; + uint16_t max_recurrent_transfer_end_date = HIVE_MAX_RECURRENT_TRANSFER_END_DATE; + uint8_t min_recurrent_transfers_recurrence = HIVE_MIN_RECURRENT_TRANSFERS_RECURRENCE; + uint16_t max_open_recurrent_transfers = HIVE_MAX_OPEN_RECURRENT_TRANSFERS; + #ifdef HIVE_ENABLE_SMT asset smt_creation_fee = asset( 1000, HBD_SYMBOL ); //< TODO: replace with HBD_asset #endif @@ -186,14 +195,6 @@ namespace hive { namespace chain { } } // hive::chain -#ifdef ENABLE_MIRA -namespace mira { - -template<> struct is_static_length< hive::chain::dynamic_global_property_object > : public boost::true_type {}; - -} // mira -#endif - FC_REFLECT( hive::chain::dynamic_global_property_object, (id) (head_block_number) @@ -219,10 +220,11 @@ FC_REFLECT( hive::chain::dynamic_global_property_object, (current_aslot) (recent_slots_filled) (participation_count) - (last_irreversible_block_num) (vote_power_reserve_rate) (delegation_return_period) (reverse_auction_seconds) + (early_voting_seconds) + (mid_voting_seconds) (available_account_subsidies) (hbd_stop_percent) (hbd_start_percent) @@ -234,6 +236,11 @@ FC_REFLECT( hive::chain::dynamic_global_property_object, (sps_fund_percent) (sps_interval_ledger) (downvote_pool_percent) + (current_remove_threshold) + (max_consecutive_recurrent_transfer_failures) + (max_recurrent_transfer_end_date) + (max_open_recurrent_transfers) + (min_recurrent_transfers_recurrence) #ifdef HIVE_ENABLE_SMT (smt_creation_fee) #endif diff --git a/libraries/chain/include/hive/chain/history_object.hpp b/libraries/chain/include/hive/chain/history_object.hpp index 4ce99df115..f6fd102f8e 100644 --- a/libraries/chain/include/hive/chain/history_object.hpp +++ b/libraries/chain/include/hive/chain/history_object.hpp @@ -90,14 +90,6 @@ namespace hive { namespace chain { > account_history_index; } } -#ifdef ENABLE_MIRA -namespace mira { - -template<> struct is_static_length< hive::chain::account_history_object > : public boost::true_type {}; - -} // mira -#endif - FC_REFLECT( hive::chain::operation_object, (id)(trx_id)(block)(trx_in_block)(op_in_trx)(virtual_op)(timestamp)(serialized_op) ) CHAINBASE_SET_INDEX_TYPE( hive::chain::operation_object, hive::chain::operation_index ) diff --git a/libraries/chain/include/hive/chain/hive_evaluator.hpp b/libraries/chain/include/hive/chain/hive_evaluator.hpp index 954aa46d16..e2e4eb31d9 100644 --- a/libraries/chain/include/hive/chain/hive_evaluator.hpp +++ b/libraries/chain/include/hive/chain/hive_evaluator.hpp @@ -30,6 +30,7 @@ HIVE_DEFINE_EVALUATOR( pow ) HIVE_DEFINE_EVALUATOR( pow2 ) HIVE_DEFINE_EVALUATOR( feed_publish ) HIVE_DEFINE_EVALUATOR( convert ) +HIVE_DEFINE_EVALUATOR( collateralized_convert ) HIVE_DEFINE_EVALUATOR( limit_order_create ) HIVE_DEFINE_EVALUATOR( limit_order_cancel ) HIVE_DEFINE_EVALUATOR( report_over_production ) @@ -67,5 +68,6 @@ HIVE_DEFINE_EVALUATOR( create_proposal ) HIVE_DEFINE_EVALUATOR(update_proposal) HIVE_DEFINE_EVALUATOR( update_proposal_votes ) HIVE_DEFINE_EVALUATOR( remove_proposal ) +HIVE_DEFINE_EVALUATOR( recurrent_transfer ) } } // hive::chain diff --git a/libraries/chain/include/hive/chain/hive_fwd.hpp b/libraries/chain/include/hive/chain/hive_fwd.hpp index a37c56a91f..e7c32b6b1d 100644 --- a/libraries/chain/include/hive/chain/hive_fwd.hpp +++ b/libraries/chain/include/hive/chain/hive_fwd.hpp @@ -7,15 +7,11 @@ #include #include -#ifdef ENABLE_MIRA -#include -#endif - namespace fc { namespace raw { -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR template inline void pack( Stream& s, const chainbase::shared_string& ss ); template diff --git a/libraries/chain/include/hive/chain/hive_object_types.hpp b/libraries/chain/include/hive/chain/hive_object_types.hpp index caed7bc99b..9507dfce15 100644 --- a/libraries/chain/include/hive/chain/hive_object_types.hpp +++ b/libraries/chain/include/hive/chain/hive_object_types.hpp @@ -54,12 +54,12 @@ enum object_type block_summary_object_type, witness_schedule_object_type, comment_object_type, - comment_content_object_type, comment_vote_object_type, witness_vote_object_type, limit_order_object_type, feed_history_object_type, convert_request_object_type, + collateralized_convert_request_object_type, liquidity_reward_balance_object_type, operation_object_type, account_history_object_type, @@ -80,6 +80,7 @@ enum object_type proposal_object_type, proposal_vote_object_type, comment_cashout_object_type, + recurrent_transfer_object_type, #ifdef HIVE_ENABLE_SMT // SMT objects smt_token_object_type, @@ -101,12 +102,12 @@ class transaction_object; class block_summary_object; class witness_schedule_object; class comment_object; -class comment_content_object; class comment_vote_object; class witness_vote_object; class limit_order_object; class feed_history_object; class convert_request_object; +class collateralized_convert_request_object; class liquidity_reward_balance_object; class operation_object; class account_history_object; @@ -125,6 +126,7 @@ class vesting_delegation_expiration_object; class pending_required_action_object; class pending_optional_action_object; class comment_cashout_object; +class recurrent_transfer_object; #ifdef HIVE_ENABLE_SMT class smt_token_object; @@ -148,12 +150,12 @@ typedef oid_ref< transaction_object > transaction_object_id_ typedef oid_ref< block_summary_object > block_summary_id_type; typedef oid_ref< witness_schedule_object > witness_schedule_id_type; typedef oid_ref< comment_object > comment_id_type; -typedef oid_ref< comment_content_object > comment_content_id_type; typedef oid_ref< comment_vote_object > comment_vote_id_type; typedef oid_ref< witness_vote_object > witness_vote_id_type; typedef oid_ref< limit_order_object > limit_order_id_type; typedef oid_ref< feed_history_object > feed_history_id_type; typedef oid_ref< convert_request_object > convert_request_id_type; +typedef oid_ref< collateralized_convert_request_object > collateralized_convert_request_id_type; typedef oid_ref< liquidity_reward_balance_object > liquidity_reward_balance_id_type; typedef oid_ref< operation_object > operation_id_type; typedef oid_ref< account_history_object > account_history_id_type; @@ -172,6 +174,7 @@ typedef oid_ref< vesting_delegation_expiration_object > vesting_delegation_exp typedef oid_ref< pending_required_action_object > pending_required_action_id_type; typedef oid_ref< pending_optional_action_object > pending_optional_action_id_type; typedef oid_ref< comment_cashout_object > comment_cashout_id_type; +typedef oid_ref< recurrent_transfer_object > recurrent_transfer_id_type; #ifdef HIVE_ENABLE_SMT typedef oid_ref< smt_token_object > smt_token_id_type; @@ -189,31 +192,17 @@ typedef oid_ref< proposal_vote_object > proposal_vote_id_type; enum bandwidth_type { post, ///< Rate limiting posting reward eligibility over time - forum, ///< Rate limiting for all forum related actins + forum, ///< Rate limiting for all forum related actions market ///< Rate limiting for all other actions }; } } //hive::chain -#ifdef ENABLE_MIRA -namespace mira { - -template< typename T > struct is_static_length< chainbase::oid< T > > : public boost::true_type {}; -template< typename T > struct is_static_length< chainbase::oid_ref< T > > : public boost::true_type {}; -template< typename T > struct is_static_length< fc::fixed_string< T > > : public boost::true_type {}; -template<> struct is_static_length< hive::protocol::account_name_type > : public boost::true_type {}; -template<> struct is_static_length< hive::protocol::asset_symbol_type > : public boost::true_type {}; -template<> struct is_static_length< hive::protocol::asset > : public boost::true_type {}; -template<> struct is_static_length< hive::protocol::price > : public boost::true_type {}; - -} // mira -#endif - namespace fc { class variant; -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR inline void to_variant( const hive::chain::shared_string& s, variant& var ) { var = fc::string( hive::chain::to_string( s ) ); @@ -226,11 +215,10 @@ inline void from_variant( const variant& var, hive::chain::shared_string& s ) } #endif - namespace raw { -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR template< typename Stream > void pack( Stream& s, const chainbase::shared_string& ss ) { @@ -299,7 +287,7 @@ void unpack( Stream& s, boost::interprocess::flat_map< K, V, C, A >& value, uint } } -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR template< typename T > T unpack_from_vector( const hive::chain::buffer_type& s ) { @@ -327,12 +315,12 @@ FC_REFLECT_ENUM( hive::chain::object_type, (block_summary_object_type) (witness_schedule_object_type) (comment_object_type) - (comment_content_object_type) (comment_vote_object_type) (witness_vote_object_type) (limit_order_object_type) (feed_history_object_type) (convert_request_object_type) + (collateralized_convert_request_object_type) (liquidity_reward_balance_object_type) (operation_object_type) (account_history_object_type) @@ -353,6 +341,7 @@ FC_REFLECT_ENUM( hive::chain::object_type, (proposal_object_type) (proposal_vote_object_type) (comment_cashout_object_type) + (recurrent_transfer_object_type) #ifdef HIVE_ENABLE_SMT (smt_token_object_type) @@ -365,7 +354,7 @@ FC_REFLECT_ENUM( hive::chain::object_type, #endif ) -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR FC_REFLECT_TYPENAME( hive::chain::shared_string ) #endif diff --git a/libraries/chain/include/hive/chain/hive_objects.hpp b/libraries/chain/include/hive/chain/hive_objects.hpp index 91bc280d3a..808e7ba8c1 100644 --- a/libraries/chain/include/hive/chain/hive_objects.hpp +++ b/libraries/chain/include/hive/chain/hive_objects.hpp @@ -6,12 +6,16 @@ #include #include +#include #include namespace hive { namespace chain { + using hive::protocol::HBD_asset; + using hive::protocol::HIVE_asset; + using hive::protocol::VEST_asset; using hive::protocol::asset; using hive::protocol::price; using hive::protocol::asset_symbol_type; @@ -28,21 +32,65 @@ namespace hive { namespace chain { public: template< typename Allocator > convert_request_object( allocator< Allocator > a, uint64_t _id, - const account_name_type& _owner, const asset& _amount, const time_point_sec& _conversion_time, uint32_t _requestid ) - : id( _id ), owner( _owner ), requestid( _requestid ), amount( _amount ), conversion_date( _conversion_time ) + const account_object& _owner, const HBD_asset& _amount, const time_point_sec& _conversion_time, uint32_t _requestid ) + : id( _id ), owner( _owner.get_id() ), amount( _amount ), requestid( _requestid ), conversion_date( _conversion_time ) {} //amount of HBD to be converted to HIVE - const asset& get_convert_amount() const { return amount; } + const HBD_asset& get_convert_amount() const { return amount; } + + //request owner + account_id_type get_owner() const { return owner; } + //request id - unique id of request among active requests of the same owner + uint32_t get_request_id() const { return requestid; } + //time of actual conversion + time_point_sec get_conversion_date() const { return conversion_date; } - account_name_type owner; //< TODO: can be replaced with account_id_type - uint32_t requestid = 0; ///< id set by owner, the owner,requestid pair must be unique - asset amount; //< TODO: can be replaced with HBD_asset - time_point_sec conversion_date; ///< at this time the feed_history_median_price * amount + private: + account_id_type owner; + HBD_asset amount; + uint32_t requestid = 0; //< id set by owner, the owner,requestid pair must be unique + time_point_sec conversion_date; //< actual conversion takes place at that time CHAINBASE_UNPACK_CONSTRUCTOR(convert_request_object); }; + /** + * This object is used to track pending requests to convert HIVE to HBD with collateral + */ + class collateralized_convert_request_object : public object< collateralized_convert_request_object_type, collateralized_convert_request_object > + { + CHAINBASE_OBJECT( collateralized_convert_request_object ); + public: + template< typename Allocator > + collateralized_convert_request_object( allocator< Allocator > a, uint64_t _id, + const account_object& _owner, const HIVE_asset& _collateral_amount, const HBD_asset& _converted_amount, + const time_point_sec& _conversion_time, uint32_t _requestid ) + : id( _id ), owner( _owner.get_id() ), collateral_amount( _collateral_amount ), converted_amount( _converted_amount ), + requestid( _requestid ), conversion_date( _conversion_time ) + {} + + //amount of HIVE collateral + const HIVE_asset& get_collateral_amount() const { return collateral_amount; } + //amount of HBD that was already given to owner + const HBD_asset& get_converted_amount() const { return converted_amount; } + + //request owner + account_id_type get_owner() const { return owner; } + //request id - unique id of request among active requests of the same owner + uint32_t get_request_id() const { return requestid; } + //time of actual conversion + time_point_sec get_conversion_date() const { return conversion_date; } + + private: + account_id_type owner; + HIVE_asset collateral_amount; + HBD_asset converted_amount; + uint32_t requestid = 0; //< id set by owner, the (owner,requestid) pair must be unique + time_point_sec conversion_date; //< actual conversion takes place at that time, excess collateral is returned to owner + + CHAINBASE_UNPACK_CONSTRUCTOR( collateralized_convert_request_object ); + }; class escrow_object : public object< escrow_object_type, escrow_object > { @@ -164,15 +212,19 @@ namespace hive { namespace chain { /** - * This object gets updated once per hour, on the hour + * This object gets updated once per hour, on the hour. Tracks price of HIVE (technically in HBD, but + * the meaning is in dollars). */ - class feed_history_object : public object< feed_history_object_type, feed_history_object > + class feed_history_object : public object< feed_history_object_type, feed_history_object > { CHAINBASE_OBJECT( feed_history_object ); public: CHAINBASE_DEFAULT_CONSTRUCTOR( feed_history_object, (price_history) ) - price current_median_history; ///< the current median of the price history, used as the base for convert operations + price current_median_history; ///< the current median of the price history, used as the base for most convert operations + price market_median_history; ///< same as current_median_history except when the latter is artificially changed upward + price current_min_history; ///< used as immediate price for collateralized conversion (after fee correction) + price current_max_history; using t_price_history = t_deque< price >; @@ -281,6 +333,58 @@ namespace hive { namespace chain { CHAINBASE_UNPACK_CONSTRUCTOR(reward_fund_object); }; + class recurrent_transfer_object : public object< recurrent_transfer_object_type, recurrent_transfer_object > + { + CHAINBASE_OBJECT( recurrent_transfer_object ); + public: + template< typename Allocator > + recurrent_transfer_object( allocator< Allocator > a, uint64_t _id, + const time_point_sec& _trigger_date, const account_object& _from, + const account_object& _to, const asset& _amount, const string& _memo, + const uint16_t _recurrence, const uint16_t _remaining_executions ) + : id( _id ), trigger_date( _trigger_date ), from_id( _from.get_id() ), to_id( _to.get_id() ), + amount( _amount ), memo( a ), recurrence( _recurrence ), remaining_executions( _remaining_executions ) + { + from_string( memo, _memo ); + } + + void update_next_trigger_date() + { + trigger_date += fc::hours(recurrence); + } + + time_point_sec get_trigger_date() const + { + return trigger_date; + } + + // if the recurrence changed, we must update the trigger_date + void set_recurrence_trigger_date( const time_point_sec& _head_block_time, uint16_t _recurrence ) + { + if ( _recurrence != recurrence ) + trigger_date = _head_block_time + fc::hours( _recurrence ); + + recurrence = _recurrence; + } + + private: + time_point_sec trigger_date; + public: + account_id_type from_id; + account_id_type to_id; + asset amount; + /// The memo is plain-text, any encryption on the memo is up to a higher level protocol. + shared_string memo; + /// How often will the payment be triggered, unit: hours + uint16_t recurrence = 0; + /// How many payment have failed in a row, at HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES the object is deleted + uint8_t consecutive_failures = 0; + /// How many executions are remaining + uint16_t remaining_executions = 0; + + CHAINBASE_UNPACK_CONSTRUCTOR(recurrent_transfer_object, (memo)); + }; + struct by_price; struct by_expiration; struct by_account; @@ -321,20 +425,43 @@ namespace hive { namespace chain { const_mem_fun< convert_request_object, convert_request_object::id_type, &convert_request_object::get_id > >, ordered_unique< tag< by_conversion_date >, composite_key< convert_request_object, - member< convert_request_object, time_point_sec, &convert_request_object::conversion_date >, + const_mem_fun< convert_request_object, time_point_sec, &convert_request_object::get_conversion_date >, const_mem_fun< convert_request_object, convert_request_object::id_type, &convert_request_object::get_id > > >, ordered_unique< tag< by_owner >, composite_key< convert_request_object, - member< convert_request_object, account_name_type, &convert_request_object::owner >, - member< convert_request_object, uint32_t, &convert_request_object::requestid > + const_mem_fun< convert_request_object, account_id_type, &convert_request_object::get_owner >, + const_mem_fun< convert_request_object, uint32_t, &convert_request_object::get_request_id > > > >, allocator< convert_request_object > > convert_request_index; + struct by_owner; + struct by_conversion_date; + typedef multi_index_container< + collateralized_convert_request_object, + indexed_by< + ordered_unique< tag< by_id >, + const_mem_fun< collateralized_convert_request_object, collateralized_convert_request_object::id_type, &collateralized_convert_request_object::get_id > >, + ordered_unique< tag< by_conversion_date >, + composite_key< collateralized_convert_request_object, + const_mem_fun< collateralized_convert_request_object, time_point_sec, &collateralized_convert_request_object::get_conversion_date >, + const_mem_fun< collateralized_convert_request_object, collateralized_convert_request_object::id_type, &collateralized_convert_request_object::get_id > + > + >, + ordered_unique< tag< by_owner >, + composite_key< collateralized_convert_request_object, + const_mem_fun< collateralized_convert_request_object, account_id_type, &collateralized_convert_request_object::get_owner >, + const_mem_fun< collateralized_convert_request_object, uint32_t, &collateralized_convert_request_object::get_request_id > + > + > + >, + allocator< collateralized_convert_request_object > + > collateralized_convert_request_index; + struct by_owner; struct by_volume_weight; @@ -478,21 +605,38 @@ namespace hive { namespace chain { allocator< reward_fund_object > > reward_fund_index; -} } // hive::chain - -#ifdef ENABLE_MIRA -namespace mira { - -template<> struct is_static_length< hive::chain::convert_request_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::escrow_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::liquidity_reward_balance_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::limit_order_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::withdraw_vesting_route_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::decline_voting_rights_request_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::reward_fund_object > : public boost::true_type {}; + struct by_from_to_id; + struct by_from_id; + struct by_trigger_date; + typedef multi_index_container< + recurrent_transfer_object, + indexed_by< + ordered_unique< tag< by_id >, + const_mem_fun< recurrent_transfer_object, recurrent_transfer_object::id_type, &recurrent_transfer_object::get_id > >, + ordered_unique< tag< by_trigger_date >, + composite_key< recurrent_transfer_object, + const_mem_fun< recurrent_transfer_object, time_point_sec, &recurrent_transfer_object::get_trigger_date >, + const_mem_fun< recurrent_transfer_object, recurrent_transfer_object::id_type, &recurrent_transfer_object::get_id > + > + >, + ordered_unique< tag< by_from_id >, + composite_key< recurrent_transfer_object, + member< recurrent_transfer_object, account_id_type, &recurrent_transfer_object::from_id >, + const_mem_fun< recurrent_transfer_object, recurrent_transfer_object::id_type, &recurrent_transfer_object::get_id > + > + >, + ordered_unique< tag< by_from_to_id >, + composite_key< recurrent_transfer_object, + member< recurrent_transfer_object, account_id_type, &recurrent_transfer_object::from_id >, + member< recurrent_transfer_object, account_id_type, &recurrent_transfer_object::to_id > + >, + composite_key_compare< std::less< account_id_type >, std::less< account_id_type > > + > + >, + allocator< recurrent_transfer_object > + > recurrent_transfer_index; -} // mira -#endif +} } // hive::chain #include #include @@ -502,13 +646,17 @@ FC_REFLECT( hive::chain::limit_order_object, CHAINBASE_SET_INDEX_TYPE( hive::chain::limit_order_object, hive::chain::limit_order_index ) FC_REFLECT( hive::chain::feed_history_object, - (id)(current_median_history)(price_history) ) + (id)(current_median_history)(market_median_history)(current_min_history)(current_max_history)(price_history) ) CHAINBASE_SET_INDEX_TYPE( hive::chain::feed_history_object, hive::chain::feed_history_index ) FC_REFLECT( hive::chain::convert_request_object, - (id)(owner)(requestid)(amount)(conversion_date) ) + (id)(owner)(amount)(requestid)(conversion_date) ) CHAINBASE_SET_INDEX_TYPE( hive::chain::convert_request_object, hive::chain::convert_request_index ) +FC_REFLECT( hive::chain::collateralized_convert_request_object, + (id)(owner)(collateral_amount)(converted_amount)(requestid)(conversion_date) ) +CHAINBASE_SET_INDEX_TYPE( hive::chain::collateralized_convert_request_object, hive::chain::collateralized_convert_request_index ) + FC_REFLECT( hive::chain::liquidity_reward_balance_object, (id)(owner)(hive_volume)(hbd_volume)(weight)(last_update) ) CHAINBASE_SET_INDEX_TYPE( hive::chain::liquidity_reward_balance_object, hive::chain::liquidity_reward_balance_index ) @@ -545,3 +693,6 @@ FC_REFLECT( hive::chain::reward_fund_object, (curation_reward_curve) ) CHAINBASE_SET_INDEX_TYPE( hive::chain::reward_fund_object, hive::chain::reward_fund_index ) + +FC_REFLECT(hive::chain::recurrent_transfer_object, (id)(trigger_date)(from_id)(to_id)(amount)(memo)(recurrence)(consecutive_failures)(remaining_executions) ) +CHAINBASE_SET_INDEX_TYPE( hive::chain::recurrent_transfer_object, hive::chain::recurrent_transfer_index ) diff --git a/libraries/chain/include/hive/chain/index.hpp b/libraries/chain/include/hive/chain/index.hpp index 3cccf9ad4c..3431484f8d 100644 --- a/libraries/chain/include/hive/chain/index.hpp +++ b/libraries/chain/include/hive/chain/index.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include #include #include @@ -59,42 +57,8 @@ void add_plugin_index( database& db ) } } -#ifdef ENABLE_MIRA - -#define HIVE_ADD_CORE_INDEX(db, index_name) \ - do { \ - hive::chain::add_core_index< index_name >( db ); \ - hive::chain::index_delegate delegate; \ - delegate.set_index_type = \ - []( database& _db, mira::index_type type, const boost::filesystem::path& p, const boost::any& cfg ) \ - { _db.get_mutable_index< index_name >().mutable_indices().set_index_type( type, p, cfg ); }; \ - db.set_index_delegate( #index_name, std::move( delegate ) ); \ - } while( false ) - -#define HIVE_ADD_PLUGIN_INDEX(db, index_name) \ - do { \ - hive::chain::add_plugin_index< index_name >( db ); \ - hive::chain::index_delegate delegate; \ - delegate.set_index_type = \ - []( database& _db, mira::index_type type, const boost::filesystem::path& p, const boost::any& cfg ) \ - { _db.get_mutable_index< index_name >().mutable_indices().set_index_type( type, p, cfg ); }; \ - db.set_index_delegate( #index_name, std::move( delegate ) ); \ - } while( false ) - -#else - -#define HIVE_ADD_CORE_INDEX(db, index_name) \ - do { \ - hive::chain::add_core_index< index_name >( db ); \ - hive::chain::index_delegate delegate; \ - db.set_index_delegate( #index_name, std::move( delegate ) ); \ - } while( false ) - -#define HIVE_ADD_PLUGIN_INDEX(db, index_name) \ - do { \ - hive::chain::add_plugin_index< index_name >( db ); \ - hive::chain::index_delegate delegate; \ - db.set_index_delegate( #index_name, std::move( delegate ) ); \ - } while( false ) +#define HIVE_ADD_CORE_INDEX(db, index_name) \ + hive::chain::add_core_index< index_name >( db ) -#endif +#define HIVE_ADD_PLUGIN_INDEX(db, index_name) \ + hive::chain::add_plugin_index< index_name >( db ) diff --git a/libraries/chain/include/hive/chain/multi_index_types.hpp b/libraries/chain/include/hive/chain/multi_index_types.hpp index 92294ced46..06327d4d26 100644 --- a/libraries/chain/include/hive/chain/multi_index_types.hpp +++ b/libraries/chain/include/hive/chain/multi_index_types.hpp @@ -1,23 +1,11 @@ #pragma once -#ifdef ENABLE_MIRA -#include -#include -#include -#include -#include -#include -#include -#include -#endif - #include +#include #include #include -#include -#include #include -#include +#include #include #include @@ -25,7 +13,6 @@ namespace hive { namespace chain { -#ifndef ENABLE_MIRA using boost::multi_index::multi_index_container; using boost::multi_index::indexed_by; using boost::multi_index::ordered_unique; @@ -41,37 +28,4 @@ inline boost::reverse_iterator< Iterator > make_reverse_iterator( Iterator itera return boost::reverse_iterator< Iterator >( iterator ); } -#else -template< typename... Args > -using multi_index_container = mira::multi_index_adapter< Args... >; -using mira::multi_index::indexed_by; -using mira::multi_index::ordered_unique; -using mira::multi_index::tag; -using mira::multi_index::member; -using mira::multi_index::composite_key; -using mira::multi_index::composite_key_compare; -using mira::multi_index::const_mem_fun; - -template< typename T1, typename T2, typename T3 > -class bmic_type : public ::mira::boost_multi_index_adapter< T1, T2, T3 > -{ -public: - using mira::boost_multi_index_adapter< T1, T2, T3 >::boost_multi_index_adapter; -}; - -template< typename T1, typename T2, typename T3 > -class mira_type : public ::mira::multi_index_container< T1, T2, T3 > -{ -public: - using mira::multi_index_container< T1, T2, T3 >::multi_index_container; -}; - -template< class Iterator > -inline Iterator make_reverse_iterator( Iterator iterator ) -{ - return iterator.reverse(); -} - -#endif - } } // hive::chain diff --git a/libraries/chain/include/hive/chain/notifications.hpp b/libraries/chain/include/hive/chain/notifications.hpp index 5a2c19d1cf..34a39c9890 100644 --- a/libraries/chain/include/hive/chain/notifications.hpp +++ b/libraries/chain/include/hive/chain/notifications.hpp @@ -54,4 +54,14 @@ struct optional_action_notification const hive::protocol::optional_automated_action& action; }; +struct comment_reward_notification +{ + comment_reward_notification( const share_type& _total_reward, share_type _author_tokens, share_type _curation_tokens ) + : total_reward( _total_reward ), author_tokens( _author_tokens ), curation_tokens( _curation_tokens ) {} + + share_type total_reward; + share_type author_tokens; + share_type curation_tokens; +}; + } } diff --git a/libraries/chain/include/hive/chain/shared_authority.hpp b/libraries/chain/include/hive/chain/shared_authority.hpp index f0b8696891..bee9881c1b 100644 --- a/libraries/chain/include/hive/chain/shared_authority.hpp +++ b/libraries/chain/include/hive/chain/shared_authority.hpp @@ -21,9 +21,6 @@ namespace hive { namespace chain { */ struct shared_authority { -#ifdef ENABLE_MIRA - shared_authority() {} -#endif template< typename Allocator > shared_authority( const authority& a, const Allocator& alloc ) : diff --git a/libraries/chain/include/hive/chain/smt_objects/smt_token_object.hpp b/libraries/chain/include/hive/chain/smt_objects/smt_token_object.hpp index 32a1bdcc77..881657b759 100644 --- a/libraries/chain/include/hive/chain/smt_objects/smt_token_object.hpp +++ b/libraries/chain/include/hive/chain/smt_objects/smt_token_object.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #ifdef HIVE_ENABLE_SMT @@ -195,7 +194,7 @@ typedef multi_index_container < const_mem_fun< smt_contribution_object, smt_contribution_object::id_type, &smt_contribution_object::get_id > > > -#ifndef IS_LOW_MEM +// #ifndef IS_LOW_MEM // indexing by contributor might cause optimization problems in the future , ordered_unique< tag< by_contributor >, composite_key< smt_contribution_object, @@ -204,7 +203,7 @@ typedef multi_index_container < member< smt_contribution_object, uint32_t, &smt_contribution_object::contribution_id > > > -#endif +// #endif >, allocator< smt_contribution_object > > smt_contribution_index; diff --git a/libraries/chain/include/hive/chain/sps_objects.hpp b/libraries/chain/include/hive/chain/sps_objects.hpp index e7c0894eda..5453755491 100644 --- a/libraries/chain/include/hive/chain/sps_objects.hpp +++ b/libraries/chain/include/hive/chain/sps_objects.hpp @@ -135,21 +135,13 @@ typedef multi_index_container< member< proposal_vote_object, uint32_t, &proposal_vote_object::proposal_id >, member< proposal_vote_object, account_name_type, &proposal_vote_object::voter > > - > + > >, allocator< proposal_vote_object > > proposal_vote_index; } } // hive::chain -#ifdef ENABLE_STD_ALLOCATOR -namespace mira { - -template<> struct is_static_length< hive::chain::proposal_vote_object > : public boost::true_type {}; - -} // mira -#endif - FC_REFLECT( hive::chain::proposal_object, (id)(proposal_id)(creator)(receiver)(start_date)(end_date)(daily_pay)(subject)(permlink)(total_votes)(removed) ) CHAINBASE_SET_INDEX_TYPE( hive::chain::proposal_object, hive::chain::proposal_index ) diff --git a/libraries/chain/include/hive/chain/util/delayed_voting.hpp b/libraries/chain/include/hive/chain/util/delayed_voting.hpp index 7caa77f1fa..210eccba72 100644 --- a/libraries/chain/include/hive/chain/util/delayed_voting.hpp +++ b/libraries/chain/include/hive/chain/util/delayed_voting.hpp @@ -43,7 +43,7 @@ class delayed_voting delayed_voting( chain::database& _db ) : db( _db ){} void add_delayed_value( const account_object& account, const time_point_sec& head_time, const ushare_type val ); - void add_votes( opt_votes_update_data_items& items, const bool withdraw_executor, const share_type val, const account_object& account ); + void add_votes( opt_votes_update_data_items& items, const bool withdraw_executor, const share_type& val, const account_object& account ); fc::optional< ushare_type > update_votes( const opt_votes_update_data_items& items, const time_point_sec& head_time ); void run( const fc::time_point_sec& head_time ); diff --git a/libraries/chain/include/hive/chain/util/remove_guard.hpp b/libraries/chain/include/hive/chain/util/remove_guard.hpp new file mode 100644 index 0000000000..1bfa250f0b --- /dev/null +++ b/libraries/chain/include/hive/chain/util/remove_guard.hpp @@ -0,0 +1,51 @@ +#pragma once + +namespace hive { namespace chain { + +class database; + +/** +* Helper class that allows you to erase/remove from any multiindex while +* adhering to given limit. Limit can be shared between different object +* types (note that cost of removing of state object does not depend on its type). +* Negative limit (default) means there is no limit. +*/ +class remove_guard +{ + public: + explicit remove_guard( int16_t _limit = -1 ) : limit( _limit ) {} + bool done() const { return limit == 0; } + + // removes given object (assuming we haven't reached limit yet), returns if it was successful + template< typename ObjectType > + bool remove( database& db, const ObjectType& obj ) + { + bool ok = step(); + if( ok ) + { + //note that we have to go through database::remove due to write lock; + //alternative would be to use database::with_write_lock and pull mutable index outside, but + //it would mean performing batch removal under continuous lock which might not be desirable + db.remove( obj ); + } + return ok; + } + + private: + bool step() + { + if( limit > 0 ) + { + --limit; + return true; + } + else + { + return !done(); + } + } + + int16_t limit; +}; + +} } // hive::chain diff --git a/libraries/chain/include/hive/chain/util/sps_helper.hpp b/libraries/chain/include/hive/chain/util/sps_helper.hpp index 8984d75762..3cb614f126 100644 --- a/libraries/chain/include/hive/chain/util/sps_helper.hpp +++ b/libraries/chain/include/hive/chain/util/sps_helper.hpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -9,89 +10,45 @@ namespace hive { namespace chain { using boost::container::flat_set; -struct sps_removing_reducer -{ - int16_t threshold; - int16_t counter = 0; - - bool done = false; - - sps_removing_reducer( int16_t _threshold = -1 ) - : threshold( _threshold ) - { - - } -}; - class sps_helper { - private: - - template< typename ByProposalType, - bool Loop, - typename ProposalObjectIterator, - typename ProposalIndex - > - static ProposalObjectIterator checker( const ProposalObjectIterator& proposal, ProposalIndex& proposalIndex, sps_removing_reducer& obj_perf ) + public: + // removes votes cast for proposals by given account (as long as we are within limit), returns if the process was successful + static bool remove_proposal_votes( const account_object& voter, const proposal_vote_index::index::type& proposal_votes, + database& db, remove_guard& obj_perf ) { - obj_perf.done = false; - - auto end = [&]() -> decltype( proposalIndex.indices(). template get< ByProposalType >().end() ) - { - return proposalIndex.indices(). template get< ByProposalType >().end(); - }; - - auto calc = [&]() - { - ++obj_perf.counter; - obj_perf.done = obj_perf.counter > obj_perf.threshold; - }; - - if( Loop ) + auto pVoteI = proposal_votes.lower_bound( boost::make_tuple( voter.name, 0 ) ); + while( pVoteI != proposal_votes.end() && pVoteI->voter == voter.name ) { - if( obj_perf.threshold < 0 ) - return end(); - - calc(); - - return end(); - } - else - { - if( obj_perf.threshold < 0 ) - return proposalIndex. template erase< ByProposalType >( proposal ); - - calc(); - - //return obj_perf.done ? end() : proposalIndex. template erase< ByProposalType >( proposal ); - if( obj_perf.done ) - return end(); - - return proposalIndex. template erase< ByProposalType >( proposal ); + const auto& vote = *pVoteI; + ++pVoteI; + if( !obj_perf.remove( db, vote ) ) + return false; } + return true; } - public: - - template< typename ByProposalType, - typename ProposalObjectIterator, - typename ProposalIndex, typename VotesIndex, typename ByVoterIdx > - static ProposalObjectIterator remove_proposal( ProposalObjectIterator& proposal, - ProposalIndex& proposalIndex, VotesIndex& votesIndex, const ByVoterIdx& byVoterIdx, sps_removing_reducer& obj_perf ) + // removes votes cast for given proposal (as long as we are within limit), returns if the process was successful + static bool remove_proposal_votes( const proposal_object& proposal, const proposal_vote_index::index::type& proposal_votes, + database& db, remove_guard& obj_perf ) { - /// Now remove all votes specific to given proposal. - auto propI = byVoterIdx.lower_bound(boost::make_tuple(proposal->proposal_id, account_name_type())); - - while(propI != byVoterIdx.end() && propI->proposal_id == proposal->proposal_id ) + auto pVoteI = proposal_votes.lower_bound( boost::make_tuple( proposal.proposal_id, account_name_type() ) ); + while( pVoteI != proposal_votes.end() && pVoteI->proposal_id == proposal.proposal_id ) { - auto result_itr = checker< ByProposalType, true/*Loop*/ >( proposal, proposalIndex, obj_perf ); - if( obj_perf.done ) - return result_itr; - - propI = votesIndex. template erase(propI); + const auto& vote = *pVoteI; + ++pVoteI; + if( !obj_perf.remove( db, vote ) ) + return false; } + return true; + } - return checker< ByProposalType, false/*Loop*/ >( proposal, proposalIndex, obj_perf ); + // removes given proposal with all related votes (as long as we are within limit), returns if the process was successful + static bool remove_proposal( const proposal_object& proposal, const proposal_vote_index::index::type& proposal_votes, + database& db, remove_guard& obj_perf ) + { + remove_proposal_votes( proposal, proposal_votes, db, obj_perf ); + return obj_perf.remove( db, proposal ); } static void remove_proposals( database& db, const flat_set& proposal_ids, const account_name_type& proposal_owner ); diff --git a/libraries/chain/include/hive/chain/util/sps_processor.hpp b/libraries/chain/include/hive/chain/util/sps_processor.hpp index 5324c72f8d..a9df2d067f 100644 --- a/libraries/chain/include/hive/chain/util/sps_processor.hpp +++ b/libraries/chain/include/hive/chain/util/sps_processor.hpp @@ -43,12 +43,8 @@ class sps_processor asset get_treasury_fund(); - asset get_daily_inflation(); - asset calculate_maintenance_budget( const time_point_sec& head_time ); - void transfer_daily_inflation_to_treasury( const asset& daily_inflation ); - void transfer_payments( const time_point_sec& head_time, asset& maintenance_budget_limit, const t_proposals& proposals ); void update_settings( const time_point_sec& head_time ); diff --git a/libraries/chain/include/hive/chain/util/tiny_asset.hpp b/libraries/chain/include/hive/chain/util/tiny_asset.hpp deleted file mode 100644 index 77136c623b..0000000000 --- a/libraries/chain/include/hive/chain/util/tiny_asset.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include - -namespace hive -{ - namespace chain - { - template< uint32_t _SYMBOL > - class tiny_asset - { - public: - - share_type amount; - - tiny_asset() {} - tiny_asset( const asset& val ) { set( val ); } - tiny_asset( asset&& val ) { set( val ); } - asset operator=( const asset& val ) { set( val ); return *this; } - asset operator=( asset&& val ) { set( val ); return *this; } - - asset operator+=( const asset& val ) { check( val ); amount += val.amount; return *this; } - asset operator-=( const asset& val ) { check( val ); amount -= val.amount; return *this; } - - operator asset() const { return to_asset(); } - - asset to_asset() const { return asset( amount, asset_symbol_type::from_asset_num( _SYMBOL ) ); } - - private: - - void set( const asset& val ) { check( val ); amount = val.amount; } - void check( const asset& val ) const { FC_ASSERT( val.symbol.asset_num == _SYMBOL ); } - }; - - using HBD_asset = tiny_asset< HIVE_ASSET_NUM_HBD >; - using HIVE_asset = tiny_asset< HIVE_ASSET_NUM_HIVE >; - using VEST_asset = tiny_asset< HIVE_ASSET_NUM_VESTS >; - - template< uint32_t _SYMBOL > - bool operator==( const tiny_asset< _SYMBOL >& obj1, const tiny_asset< _SYMBOL >& obj2 ) { return obj1.to_asset() == obj2.to_asset(); } - - template< uint32_t _SYMBOL > - bool operator!=( const tiny_asset< _SYMBOL >& obj1, const tiny_asset< _SYMBOL >& obj2 ) { return !( obj1.to_asset() == obj2.to_asset() ); } - - template< uint32_t _SYMBOL > - asset operator-( const tiny_asset< _SYMBOL >& obj1) { return -static_cast< asset >( obj1 ); } - - } -} - -FC_REFLECT( hive::chain::HBD_asset, (amount) ) -FC_REFLECT( hive::chain::HIVE_asset, (amount) ) -FC_REFLECT( hive::chain::VEST_asset, (amount) ) \ No newline at end of file diff --git a/libraries/chain/include/hive/chain/witness_objects.hpp b/libraries/chain/include/hive/chain/witness_objects.hpp index 5ea7faa0e9..7e1930589a 100644 --- a/libraries/chain/include/hive/chain/witness_objects.hpp +++ b/libraries/chain/include/hive/chain/witness_objects.hpp @@ -15,6 +15,9 @@ namespace hive { namespace chain { using hive::protocol::version; using hive::protocol::hardfork_version; using hive::protocol::price; + using hive::protocol::HBD_asset; + using hive::protocol::HIVE_asset; + using hive::protocol::VEST_asset; using hive::protocol::asset; using hive::protocol::asset_symbol_type; using hive::chain::util::rd_dynamics_params; @@ -274,15 +277,6 @@ namespace hive { namespace chain { } } -#ifdef ENABLE_MIRA -namespace mira { - -template<> struct is_static_length< hive::chain::witness_vote_object > : public boost::true_type {}; -template<> struct is_static_length< hive::chain::witness_schedule_object > : public boost::true_type {}; - -} // mira -#endif - FC_REFLECT_ENUM( hive::chain::witness_object::witness_schedule_type, (elected)(timeshare)(miner)(none) ) FC_REFLECT( hive::chain::chain_properties, diff --git a/libraries/chain/index.cpp b/libraries/chain/index.cpp index 8cb43d8477..51a72434c0 100644 --- a/libraries/chain/index.cpp +++ b/libraries/chain/index.cpp @@ -1,68 +1,69 @@ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace hive { namespace chain { - -void initialize_core_indexes( database& db ) -{ - HIVE_ADD_CORE_INDEX(db, dynamic_global_property_index); - HIVE_ADD_CORE_INDEX(db, account_index); - HIVE_ADD_CORE_INDEX(db, account_metadata_index); - HIVE_ADD_CORE_INDEX(db, account_authority_index); - HIVE_ADD_CORE_INDEX(db, witness_index); - HIVE_ADD_CORE_INDEX(db, transaction_index); - HIVE_ADD_CORE_INDEX(db, block_summary_index); - HIVE_ADD_CORE_INDEX(db, witness_schedule_index); - HIVE_ADD_CORE_INDEX(db, comment_index); - HIVE_ADD_CORE_INDEX(db, comment_content_index); - HIVE_ADD_CORE_INDEX(db, comment_vote_index); - HIVE_ADD_CORE_INDEX(db, witness_vote_index); - HIVE_ADD_CORE_INDEX(db, limit_order_index); - HIVE_ADD_CORE_INDEX(db, feed_history_index); - HIVE_ADD_CORE_INDEX(db, convert_request_index); - HIVE_ADD_CORE_INDEX(db, liquidity_reward_balance_index); - HIVE_ADD_CORE_INDEX(db, operation_index); - HIVE_ADD_CORE_INDEX(db, account_history_index); - HIVE_ADD_CORE_INDEX(db, hardfork_property_index); - HIVE_ADD_CORE_INDEX(db, withdraw_vesting_route_index); - HIVE_ADD_CORE_INDEX(db, owner_authority_history_index); - HIVE_ADD_CORE_INDEX(db, account_recovery_request_index); - HIVE_ADD_CORE_INDEX(db, change_recovery_account_request_index); - HIVE_ADD_CORE_INDEX(db, escrow_index); - HIVE_ADD_CORE_INDEX(db, savings_withdraw_index); - HIVE_ADD_CORE_INDEX(db, decline_voting_rights_request_index); - HIVE_ADD_CORE_INDEX(db, reward_fund_index); - HIVE_ADD_CORE_INDEX(db, vesting_delegation_index); - HIVE_ADD_CORE_INDEX(db, vesting_delegation_expiration_index); - HIVE_ADD_CORE_INDEX(db, pending_required_action_index); - HIVE_ADD_CORE_INDEX(db, pending_optional_action_index); -#ifdef HIVE_ENABLE_SMT - HIVE_ADD_CORE_INDEX(db, smt_token_index); - HIVE_ADD_CORE_INDEX(db, account_regular_balance_index); - HIVE_ADD_CORE_INDEX(db, account_rewards_balance_index); - HIVE_ADD_CORE_INDEX(db, nai_pool_index); - HIVE_ADD_CORE_INDEX(db, smt_token_emissions_index); - HIVE_ADD_CORE_INDEX(db, smt_contribution_index); - HIVE_ADD_CORE_INDEX(db, smt_ico_index); -#endif - HIVE_ADD_CORE_INDEX(db, proposal_index); - HIVE_ADD_CORE_INDEX(db, proposal_vote_index); - HIVE_ADD_CORE_INDEX(db, comment_cashout_index); -} - -index_info::index_info() {} -index_info::~index_info() {} - -} } + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hive { namespace chain { + +void initialize_core_indexes( database& db ) +{ + HIVE_ADD_CORE_INDEX(db, dynamic_global_property_index); + HIVE_ADD_CORE_INDEX(db, account_index); + HIVE_ADD_CORE_INDEX(db, account_metadata_index); + HIVE_ADD_CORE_INDEX(db, account_authority_index); + HIVE_ADD_CORE_INDEX(db, witness_index); + HIVE_ADD_CORE_INDEX(db, transaction_index); + HIVE_ADD_CORE_INDEX(db, block_summary_index); + HIVE_ADD_CORE_INDEX(db, witness_schedule_index); + HIVE_ADD_CORE_INDEX(db, comment_index); + HIVE_ADD_CORE_INDEX(db, comment_vote_index); + HIVE_ADD_CORE_INDEX(db, witness_vote_index); + HIVE_ADD_CORE_INDEX(db, limit_order_index); + HIVE_ADD_CORE_INDEX(db, feed_history_index); + HIVE_ADD_CORE_INDEX(db, convert_request_index); + HIVE_ADD_CORE_INDEX(db, collateralized_convert_request_index); + HIVE_ADD_CORE_INDEX(db, liquidity_reward_balance_index); + HIVE_ADD_CORE_INDEX(db, operation_index); + HIVE_ADD_CORE_INDEX(db, account_history_index); + HIVE_ADD_CORE_INDEX(db, hardfork_property_index); + HIVE_ADD_CORE_INDEX(db, withdraw_vesting_route_index); + HIVE_ADD_CORE_INDEX(db, owner_authority_history_index); + HIVE_ADD_CORE_INDEX(db, account_recovery_request_index); + HIVE_ADD_CORE_INDEX(db, change_recovery_account_request_index); + HIVE_ADD_CORE_INDEX(db, escrow_index); + HIVE_ADD_CORE_INDEX(db, savings_withdraw_index); + HIVE_ADD_CORE_INDEX(db, decline_voting_rights_request_index); + HIVE_ADD_CORE_INDEX(db, reward_fund_index); + HIVE_ADD_CORE_INDEX(db, vesting_delegation_index); + HIVE_ADD_CORE_INDEX(db, vesting_delegation_expiration_index); + HIVE_ADD_CORE_INDEX(db, pending_required_action_index); + HIVE_ADD_CORE_INDEX(db, pending_optional_action_index); +#ifdef HIVE_ENABLE_SMT + HIVE_ADD_CORE_INDEX(db, smt_token_index); + HIVE_ADD_CORE_INDEX(db, account_regular_balance_index); + HIVE_ADD_CORE_INDEX(db, account_rewards_balance_index); + HIVE_ADD_CORE_INDEX(db, nai_pool_index); + HIVE_ADD_CORE_INDEX(db, smt_token_emissions_index); + HIVE_ADD_CORE_INDEX(db, smt_contribution_index); + HIVE_ADD_CORE_INDEX(db, smt_ico_index); +#endif + HIVE_ADD_CORE_INDEX(db, proposal_index); + HIVE_ADD_CORE_INDEX(db, proposal_vote_index); + HIVE_ADD_CORE_INDEX(db, comment_cashout_index); + HIVE_ADD_CORE_INDEX(db, recurrent_transfer_index); +} + +index_info::index_info() {} +index_info::~index_info() {} + +} } diff --git a/libraries/chain/smt_objects/smt_market_maker.cpp b/libraries/chain/smt_objects/smt_market_maker.cpp index 45a78a0956..38c509d4bd 100644 --- a/libraries/chain/smt_objects/smt_market_maker.cpp +++ b/libraries/chain/smt_objects/smt_market_maker.cpp @@ -23,6 +23,7 @@ static std::vector< rational_u64 > load_mm_ticks() std::vector< std::pair< uint64_t, uint64_t > > mm_ticks_pairs; fc::from_variant( mm_ticks_var, mm_ticks_pairs ); std::vector< rational_u64 > mm_ticks; + mm_ticks.reserve( mm_ticks_pairs.size() ); for( const std::pair< uint64_t, uint64_t >& p : mm_ticks_pairs ) mm_ticks.emplace_back( p ); return mm_ticks; diff --git a/libraries/chain/sps_evaluator.cpp b/libraries/chain/sps_evaluator.cpp index 655ba3226a..a38c36f246 100644 --- a/libraries/chain/sps_evaluator.cpp +++ b/libraries/chain/sps_evaluator.cpp @@ -98,11 +98,28 @@ void update_proposal_evaluator::do_apply( const update_proposal_operation& o ) FC_ASSERT(o.daily_pay <= proposal.daily_pay, "You cannot increase the daily pay"); + const update_proposal_end_date* ed = nullptr; + if (_db.has_hardfork(HIVE_HARDFORK_1_25)) { + FC_ASSERT( o.extensions.size() < 2, "Cannot have more than 1 extension"); + // NOTE: This assumes there is only one extension and it's of type proposal_end_date, if you add more, update this code + if (o.extensions.size() == 1) { + ed = &(o.extensions.begin()->get()); + FC_ASSERT(ed->end_date <= proposal.end_date, "You cannot increase the end date of the proposal"); + FC_ASSERT(ed->end_date > proposal.start_date, "The new end date must be after the start date"); + } + } else { + FC_ASSERT( o.extensions.empty() , "Cannot set extensions"); + } + _db.modify( proposal, [&]( proposal_object& p ) { p.daily_pay = o.daily_pay; p.subject = o.subject.c_str(); p.permlink = o.permlink.c_str(); + + if (_db.has_hardfork(HIVE_HARDFORK_1_25) && ed != nullptr) { + p.end_date = ed->end_date; + } }); } FC_CAPTURE_AND_RETHROW( (o) ) @@ -117,6 +134,9 @@ void update_proposal_votes_evaluator::do_apply( const update_proposal_votes_oper const auto& pidx = _db.get_index< proposal_index >().indices().get< by_proposal_id >(); const auto& pvidx = _db.get_index< proposal_vote_index >().indices().get< by_voter_proposal >(); + const auto& voter = _db.get_account(o.voter); + _db.modify( voter, [&](account_object& a) { a.update_governance_vote_expiration_ts(_db.head_block_time()); }); + for( const auto pid : o.proposal_ids ) { //checking if proposal id exists @@ -124,6 +144,16 @@ void update_proposal_votes_evaluator::do_apply( const update_proposal_votes_oper if( found_id == pidx.end() || found_id->removed ) continue; + if( _db.has_hardfork( HIVE_HARDFORK_1_25 ) ) + { + /* + In the future is possible a situation, when it will be thousands proposals and some account will vote on each proposal. + During the account's deactivation, all votes have to be removed immediately, so it's a risk of potential performance issue. + Better it not to allow vote on expired proposal. + */ + FC_ASSERT(_db.head_block_time() <= found_id->end_date, "Voting on expired proposals is not allowed..."); + } + auto found = pvidx.find( boost::make_tuple( o.voter, pid ) ); if( o.approve ) @@ -151,31 +181,32 @@ void remove_proposal_evaluator::do_apply(const remove_proposal_operation& op) { FC_ASSERT( _db.has_hardfork( HIVE_PROPOSALS_HARDFORK ), "Proposals functionality not enabled until hardfork ${hf}", ("hf", HIVE_PROPOSALS_HARDFORK) ); + // Remove proposals and related votes... sps_helper::remove_proposals( _db, op.proposal_ids, op.proposal_owner ); /* - Because of performance removing proposals are restricted due to the `sps_remove_threshold` threshold. - Therefore all proposals are marked with flag `removed` and `end_date` is moved beyond 'head_time + HIVE_PROPOSAL_MAINTENANCE_CLEANUP` + ...For performance reasons and the fact that proposal votes can accumulate over time but need to be removed along with proposals, + process of proposal removal is subject to `common_remove_threshold`. Proposals and votes are physically removed above, however if + some remain due to threshold being reached, the rest is marked with `removed` flag, to be actually removed during regular per-block cycles. + The `end_date` is moved to `head_time - HIVE_PROPOSAL_MAINTENANCE_CLEANUP` so the proposals are ready to be removed immediately + (see proposal_object::get_end_date_with_delay() - there was a short window when proposal was expired but still "alive" to avoid corner cases) flag `removed` - it's information for 'sps_api' plugin moving `end_date` - triggers the algorithm in `sps_processor::remove_proposals` When automatic actions will be introduced, this code will disappear. */ + auto new_end_date = _db.head_block_time() - fc::seconds( HIVE_PROPOSAL_MAINTENANCE_CLEANUP ); for( const auto pid : op.proposal_ids ) { - const auto& pidx = _db.get_index< proposal_index >().indices().get< by_proposal_id >(); + const auto& pidx = _db.get_index< proposal_index, by_proposal_id >(); auto found_id = pidx.find( pid ); if( found_id == pidx.end() || found_id->removed ) continue; - - _db.modify( *found_id, [&]( proposal_object& proposal ) + + _db.modify( *found_id, [new_end_date]( proposal_object& proposal ) { proposal.removed = true; - - auto head_date = _db.head_block_time(); - auto new_end_date = head_date - fc::seconds( HIVE_PROPOSAL_MAINTENANCE_CLEANUP ); - proposal.end_date = new_end_date; } ); } diff --git a/libraries/chain/util/delayed_voting.cpp b/libraries/chain/util/delayed_voting.cpp index 61e0d04463..3517ded9ff 100644 --- a/libraries/chain/util/delayed_voting.cpp +++ b/libraries/chain/util/delayed_voting.cpp @@ -3,8 +3,6 @@ namespace hive { namespace chain { -using hive::protocol::asset; - void delayed_voting::add_delayed_value( const account_object& account, const time_point_sec& head_time, const ushare_type val ) { db.modify( account, [&]( account_object& a ) @@ -24,7 +22,7 @@ void delayed_voting::erase_delayed_value( const account_object& account, const u } ); } -void delayed_voting::add_votes( opt_votes_update_data_items& items, const bool withdraw_executor, const share_type val, const account_object& account ) +void delayed_voting::add_votes( opt_votes_update_data_items& items, const bool withdraw_executor, const share_type& val, const account_object& account ) { if( !items.valid() || val == 0 ) return; diff --git a/libraries/chain/util/impacted.cpp b/libraries/chain/util/impacted.cpp index 4e1c8e7857..6d3872c72f 100644 --- a/libraries/chain/util/impacted.cpp +++ b/libraries/chain/util/impacted.cpp @@ -38,6 +38,12 @@ struct get_impacted_account_visitor _impacted.insert( op.creator ); } + void operator()( const account_created_operation& op ) + { + _impacted.insert( op.creator ); + _impacted.insert( op.new_account_name ); + } + void operator()( const comment_operation& op ) { _impacted.insert( op.author ); @@ -110,7 +116,8 @@ struct get_impacted_account_visitor void operator()( const account_witness_proxy_operation& op ) { _impacted.insert( op.account ); - _impacted.insert( op.proxy ); + if ( !op.is_clearing_proxy() ) + _impacted.insert( op.proxy ); } void operator()( const feed_publish_operation& op ) @@ -186,6 +193,11 @@ struct get_impacted_account_visitor _impacted.insert( op.new_account_name ); } + void operator()( const recurrent_transfer_operation& op ) + { + _impacted.insert( op.from ); + _impacted.insert( op.to ); + } // vops @@ -214,12 +226,33 @@ struct get_impacted_account_visitor _impacted.insert( op.owner ); } + void operator()( const fill_collateralized_convert_request_operation& op ) + { + _impacted.insert( op.owner ); + } + void operator()( const fill_vesting_withdraw_operation& op ) { _impacted.insert( op.from_account ); _impacted.insert( op.to_account ); } + void operator()( const transfer_to_vesting_completed_operation& op ) + { + _impacted.insert( op.from_account ); + _impacted.insert( op.to_account ); + } + + void operator()( const pow_reward_operation& op ) + { + _impacted.insert( op.worker ); + } + + void operator()( const vesting_shares_split_operation& op ) + { + _impacted.insert( op.owner ); + } + void operator()( const shutdown_witness_operation& op ) { _impacted.insert( op.owner ); @@ -343,6 +376,37 @@ struct get_impacted_account_visitor { _impacted.insert( HIVE_NULL_ACCOUNT ); } + + void operator()( const expired_account_notification_operation& op ) + { + _impacted.insert( op.account ); + } + + void operator()( const changed_recovery_account_operation& op ) + { + _impacted.insert( op.account ); + _impacted.insert( op.old_recovery_account ); + _impacted.insert( op.new_recovery_account ); + } + + void operator()( const system_warning_operation& op ) + { + _impacted.insert( HIVE_INIT_MINER_NAME ); + } + + + void operator()( const fill_recurrent_transfer_operation& op ) + { + _impacted.insert( op.from ); + _impacted.insert( op.to ); + } + + void operator()( const failed_recurrent_transfer_operation& op ) + { + _impacted.insert( op.from ); + _impacted.insert( op.to ); + } + //void operator()( const operation& op ){} }; diff --git a/libraries/chain/util/rd.cpp b/libraries/chain/util/rd.cpp index c9ccc08fc5..1c209a87f6 100644 --- a/libraries/chain/util/rd.cpp +++ b/libraries/chain/util/rd.cpp @@ -88,7 +88,7 @@ void rd_setup_dynamics_params( // So we can set x = ceil((b << s) / d) = ((b << s) + (d-1)) / d. // Worst-case assert for the following is d=1, in which case b << s must be less than 2^64 - static_assert( HIVE_RD_MAX_BUDGET < (uint64_t(1) << (64-HIVE_RD_DECAY_DENOM_SHIFT)), + static_assert( uint64_t(HIVE_RD_MAX_BUDGET) < (uint64_t(1) << (64-HIVE_RD_DECAY_DENOM_SHIFT)), "Computation of temp could overflow here, set smaller HIVE_RD_MAX_BUDGET" ); fc::uint128_t temp = user_params.budget_per_time_unit; diff --git a/libraries/chain/util/sps_helper.cpp b/libraries/chain/util/sps_helper.cpp index b5b5655aa7..7228fc3516 100644 --- a/libraries/chain/util/sps_helper.cpp +++ b/libraries/chain/util/sps_helper.cpp @@ -9,13 +9,10 @@ void sps_helper::remove_proposals( database& db, const flat_set& propos if( proposal_ids.empty() ) return; - auto& proposalIndex = db.get_mutable_index< proposal_index >(); - auto& byPropIdIdx = proposalIndex.indices().get< by_proposal_id >(); + auto& byPropIdIdx = db.get_index< proposal_index, by_proposal_id >(); + auto& byVoterIdx = db.get_index< proposal_vote_index, by_proposal_voter >(); - auto& votesIndex = db.get_mutable_index< proposal_vote_index >(); - auto& byVoterIdx = votesIndex.indices().get< by_proposal_voter >(); - - sps_removing_reducer obj_perf( db.get_sps_remove_threshold() ); + remove_guard obj_perf( db.get_remove_threshold() ); for(auto pid : proposal_ids) { @@ -26,8 +23,8 @@ void sps_helper::remove_proposals( database& db, const flat_set& propos FC_ASSERT(foundPosI->creator == proposal_owner, "Only proposal owner can remove it..."); - remove_proposal< by_proposal_id >( foundPosI, proposalIndex, votesIndex, byVoterIdx, obj_perf ); - if( obj_perf.done ) + remove_proposal( *foundPosI, byVoterIdx, db, obj_perf ); + if( obj_perf.done() ) break; } diff --git a/libraries/chain/util/sps_processor.cpp b/libraries/chain/util/sps_processor.cpp index a51a4c862b..7dd2328656 100644 --- a/libraries/chain/util/sps_processor.cpp +++ b/libraries/chain/util/sps_processor.cpp @@ -12,7 +12,6 @@ using hive::chain::proposal_index; using hive::chain::proposal_id_type; using hive::chain::proposal_vote_index; using hive::chain::by_proposal_voter; -using hive::chain::by_voter_proposal; using hive::protocol::proposal_pay_operation; using hive::chain::sps_helper; using hive::chain::dynamic_global_property_object; @@ -23,43 +22,45 @@ const std::string sps_processor::calculating_name = "sps_processor_calculate"; bool sps_processor::is_maintenance_period( const time_point_sec& head_time ) const { - return db.get_dynamic_global_properties().next_maintenance_time <= head_time; + auto due_time = db.get_dynamic_global_properties().next_maintenance_time; + return due_time <= head_time; } bool sps_processor::is_daily_maintenance_period( const time_point_sec& head_time ) const { /// No DHF conversion until HF24 ! - return db.has_hardfork(HIVE_HARDFORK_1_24) && db.get_dynamic_global_properties().next_daily_maintenance_time <= head_time; + if( !db.has_hardfork( HIVE_HARDFORK_1_24 ) ) + return false; + auto due_time = db.get_dynamic_global_properties().next_daily_maintenance_time; + return due_time <= head_time; } void sps_processor::remove_proposals( const time_point_sec& head_time ) { FC_TODO("implement proposal removal based on automatic actions") - auto& proposalIndex = db.get_mutable_index< proposal_index >(); - auto& byEndDateIdx = proposalIndex.indices().get< by_end_date >(); - auto& votesIndex = db.get_mutable_index< proposal_vote_index >(); - auto& byVoterIdx = votesIndex.indices().get< by_proposal_voter >(); + auto& byEndDateIdx = db.get_index< proposal_index, by_end_date >(); + auto& byVoterIdx = db.get_index< proposal_vote_index, by_proposal_voter >(); - auto found = byEndDateIdx.upper_bound( head_time ); + auto end = byEndDateIdx.upper_bound( head_time ); auto itr = byEndDateIdx.begin(); - sps_removing_reducer obj_perf( db.get_sps_remove_threshold() ); + remove_guard obj_perf( db.get_remove_threshold() ); - while( itr != found ) + while( itr != end ) { + const auto& proposal = *itr; + ++itr; /* - It was decided that automatic removing of old proposals will be blocked. - In result it will be possible to find expired proposals, by API call `list_proposals` with `expired` flag. - Maybe in the future removing will be re-enabled. - - Proposals can be removed only by explicit call of `remove_proposal_operation`. + It was decided that automatic removing of old proposals is to be blocked in order to allow looking for + expired proposals (by API call `list_proposals` with `expired` flag). Maybe in the future removing will be re-enabled. + For now proposals can be removed only by explicit call of `remove_proposal_operation` - that operation removes some + data and if threshold is reached, remaining proposals are marked as `removed` and are removed in regular per-block + cycles here. */ - if( itr->removed ) - itr = sps_helper::remove_proposal< by_end_date >( itr, proposalIndex, votesIndex, byVoterIdx, obj_perf ); - else - ++itr; - if( obj_perf.done ) + if( proposal.removed ) + sps_helper::remove_proposal( proposal, byVoterIdx, db, obj_perf ); + if( obj_perf.done() ) break; } } @@ -68,6 +69,7 @@ void sps_processor::find_proposals( const time_point_sec& head_time, t_proposals { const auto& pidx = db.get_index< proposal_index >().indices().get< by_start_date >(); + FC_TODO("avoid scanning all proposals by use of end_date index, don't list future proposals - use index directly"); std::for_each( pidx.begin(), pidx.upper_bound( head_time ), [&]( auto& proposal ) { if( head_time >= proposal.start_date && head_time <= proposal.end_date ) @@ -92,7 +94,7 @@ uint64_t sps_processor::calculate_votes( uint32_t pid ) const auto& _voter = db.get_account( found->voter ); //If _voter has set proxy, then his votes aren't taken into consideration - if( _voter.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ) + if( !_voter.has_proxy() ) { auto sum = _voter.witness_vote_weight(); ret += sum.value; @@ -137,61 +139,22 @@ asset sps_processor::get_treasury_fund() return treasury_account.get_hbd_balance(); } -asset sps_processor::get_daily_inflation() -{ - FC_TODO( "to invent how to get inflation needed for HIVE_TREASURY_ACCOUNT" ) - return asset( 0, HBD_SYMBOL ); -} - asset sps_processor::calculate_maintenance_budget( const time_point_sec& head_time ) { //Get funds from 'treasury' account ( treasury_fund ) asset treasury_fund = get_treasury_fund(); - //Get daily proposal inflation ( daily_proposal_inflation ) - asset daily_inflation = get_daily_inflation(); - - FC_ASSERT( treasury_fund.symbol == daily_inflation.symbol, "symbols must be the same" ); - //Calculate budget for given maintenance period uint32_t passed_time_seconds = ( head_time - db.get_dynamic_global_properties().last_budget_time ).to_seconds(); //Calculate daily_budget_limit - int64_t daily_budget_limit = treasury_fund.amount.value / total_amount_divider + daily_inflation.amount.value; + int64_t daily_budget_limit = treasury_fund.amount.value / total_amount_divider; daily_budget_limit = ( ( uint128_t( passed_time_seconds ) * daily_budget_limit ) / daily_seconds ).to_uint64(); - //Transfer daily_proposal_inflation to `treasury account` - transfer_daily_inflation_to_treasury( daily_inflation ); - return asset( daily_budget_limit, treasury_fund.symbol ); } -void sps_processor::transfer_daily_inflation_to_treasury( const asset& daily_inflation ) -{ - /* - Now `daily_inflation` is always zero. Take a look at `get_daily_inflation` - - Comment from Michael Vandeberg: - - Is this printing new HBD? - - That's not how we have handled inflation in the past. - Either inflation should be paid, per block, - in to the treasury account in database::process_funds or added to a temp fund in the dgpo - that is then transferred in to the treasury account during maintenance. - */ - FC_TODO( "to choose how to transfer inflation into HIVE_TREASURY_ACCOUNT" ) - // Ifdeffing this out so that no inflation is accidentally created on main net. -#ifdef IS_TEST_NET - if( daily_inflation.amount.value > 0 ) - { - const auto& treasury_account = db.get_treasury(); - db.adjust_balance( treasury_account, daily_inflation ); - } -#endif -} - void sps_processor::transfer_payments( const time_point_sec& head_time, asset& maintenance_budget_limit, const t_proposals& proposals ) { if( maintenance_budget_limit.amount.value == 0 ) @@ -200,13 +163,12 @@ void sps_processor::transfer_payments( const time_point_sec& head_time, asset& m const auto& treasury_account = db.get_treasury(); uint32_t passed_time_seconds = ( head_time - db.get_dynamic_global_properties().last_budget_time ).to_seconds(); - uint128_t ratio = ( passed_time_seconds * HIVE_100_PERCENT ) / daily_seconds; auto processing = [this, &treasury_account]( const proposal_object& _item, const asset& payment ) { const auto& receiver_account = db.get_account( _item.receiver ); - operation vop = proposal_pay_operation( _item.receiver, db.get_treasury_name(), payment, db.get_current_trx(), db.get_current_op_in_trx() ); + operation vop = proposal_pay_operation( _item.proposal_id, _item.receiver, db.get_treasury_name(), payment, db.get_current_trx(), db.get_current_op_in_trx() ); /// Push vop to be recorded by other parts (like AH plugin etc.) db.push_virtual_operation(vop); /// Virtual ops have no evaluators, so operation must be immediately "evaluated" @@ -222,7 +184,16 @@ void sps_processor::transfer_payments( const time_point_sec& head_time, asset& m if( _item.total_votes == 0 ) break; - asset period_pay = asset( ( ratio * _item.daily_pay.amount.value ).to_uint64() / HIVE_100_PERCENT, _item.daily_pay.symbol ); + asset period_pay; + if( db.has_hardfork(HIVE_HARDFORK_1_25) ) + { + period_pay = asset((( passed_time_seconds * _item.daily_pay.amount.value ) / daily_seconds ), _item.daily_pay.symbol ); + } + else + { + uint128_t ratio = ( passed_time_seconds * HIVE_100_PERCENT ) / daily_seconds; + period_pay = asset( ( ratio * _item.daily_pay.amount.value ).to_uint64() / HIVE_100_PERCENT, _item.daily_pay.symbol ); + } if( period_pay >= maintenance_budget_limit ) { @@ -241,6 +212,8 @@ void sps_processor::update_settings( const time_point_sec& head_time ) { db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dgpo ) { + //shifting from current time instead of proper maintenance time causes drift + //when maintenance block was missed, but the fix is problematic - see MR!168 for details _dgpo.next_maintenance_time = head_time + fc::seconds( HIVE_PROPOSAL_MAINTENANCE_PERIOD ); _dgpo.last_budget_time = head_time; } ); @@ -353,6 +326,8 @@ void sps_processor::convert_funds( const block_notification& note ) db.modify( db.get_dynamic_global_properties(), [&]( dynamic_global_property_object& _dgpo ) { + //shifting from current time instead of proper maintenance time causes drift + //when maintenance block was missed, but the fix is problematic - see MR!168 for details _dgpo.next_daily_maintenance_time = note.block.timestamp + fc::seconds( HIVE_DAILY_PROPOSAL_MAINTENANCE_PERIOD ); } ); diff --git a/libraries/chainbase/CMakeLists.txt b/libraries/chainbase/CMakeLists.txt index 66976280fe..f9641d9859 100644 --- a/libraries/chainbase/CMakeLists.txt +++ b/libraries/chainbase/CMakeLists.txt @@ -23,16 +23,17 @@ IF( WIN32 ) set(BOOST_ALL_DYN_LINK OFF) # force dynamic linking for all libraries ENDIF(WIN32) +cmake_policy(SET CMP0057 NEW) FIND_PACKAGE(Boost 1.57 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) if( APPLE ) # Apple Specific Options Here message( STATUS "Configuring ChainBase on OS X" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -stdlib=libc++ -Wall -Wno-conversion" ) + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -stdlib=libc++ -Wall -Wno-conversion" ) else( APPLE ) # Linux Specific Options Here message( STATUS "Configuring ChainBase on Linux" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11 -Wall" ) + set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wall" ) set( rt_library rt ) set( pthread_library pthread) if ( FULL_STATIC_BUILD ) diff --git a/libraries/chainbase/include/chainbase/allocators.hpp b/libraries/chainbase/include/chainbase/allocators.hpp index 63219ec68b..062e11f238 100644 --- a/libraries/chainbase/include/chainbase/allocators.hpp +++ b/libraries/chainbase/include/chainbase/allocators.hpp @@ -21,7 +21,10 @@ namespace chainbase { namespace bip = boost::interprocess; - #ifdef ENABLE_MIRA + // If you want to use the std allocator instead of the boost::interprocess one (for testing purposes) uncomment the following line: + // #define ENABLE_STD_ALLOCATOR // ENABLE_STD_ALLOCATOR option has been removed from CMake file. + + #ifdef ENABLE_STD_ALLOCATOR template< typename T > using allocator = std::allocator< T >; @@ -37,19 +40,19 @@ namespace chainbase { typedef boost::unique_lock< read_write_mutex > write_lock; - #ifdef ENABLE_MIRA - #define _ENABLE_MIRA 1 + #ifdef ENABLE_STD_ALLOCATOR + #define _ENABLE_STD_ALLOCATOR 1 #else - #define _ENABLE_MIRA 0 + #define _ENABLE_STD_ALLOCATOR 0 #endif - using shared_string = std::conditional< _ENABLE_MIRA, + using shared_string = std::conditional< _ENABLE_STD_ALLOCATOR, std::string, bip::basic_string< char, std::char_traits< char >, allocator< char > > >::type; template - using t_vector = typename std::conditional< _ENABLE_MIRA, + using t_vector = typename std::conditional< _ENABLE_STD_ALLOCATOR, std::vector >, bip::vector > >::type; @@ -61,19 +64,19 @@ namespace chainbase { using t_allocator_pair = allocator< t_pair< const FIRST_TYPE, SECOND_TYPE > >; template< typename KEY_TYPE, typename LESS_FUNC = std::less> - using t_flat_set = typename std::conditional< _ENABLE_MIRA, + using t_flat_set = typename std::conditional< _ENABLE_STD_ALLOCATOR, boost::container::flat_set< KEY_TYPE, LESS_FUNC, allocator< KEY_TYPE > >, bip::flat_set< KEY_TYPE, LESS_FUNC, allocator< KEY_TYPE > > >::type; template< typename KEY_TYPE, typename VALUE_TYPE, typename LESS_FUNC = std::less> - using t_flat_map = typename std::conditional< _ENABLE_MIRA, + using t_flat_map = typename std::conditional< _ENABLE_STD_ALLOCATOR, boost::container::flat_map< KEY_TYPE, VALUE_TYPE, LESS_FUNC, allocator< t_pair< KEY_TYPE, VALUE_TYPE > > >, bip::flat_map< KEY_TYPE, VALUE_TYPE, LESS_FUNC, allocator< t_pair< KEY_TYPE, VALUE_TYPE > > > >::type; template< typename T > - using t_deque = typename std::conditional< _ENABLE_MIRA, + using t_deque = typename std::conditional< _ENABLE_STD_ALLOCATOR, std::deque< T, allocator< T > >, bip::deque< T, allocator< T > > >::type; diff --git a/libraries/chainbase/include/chainbase/chainbase.hpp b/libraries/chainbase/include/chainbase/chainbase.hpp index c99ebcbbd6..dbaaa8da7d 100644 --- a/libraries/chainbase/include/chainbase/chainbase.hpp +++ b/libraries/chainbase/include/chainbase/chainbase.hpp @@ -23,6 +23,8 @@ #include #include +#include + #include #include #include @@ -43,6 +45,15 @@ #define CHAINBASE_REQUIRE_WRITE_LOCK(m, t) #endif +//redirect exceptions from chainbase to the same place as the one from FC_ASSERT +#define CHAINBASE_THROW_EXCEPTION( exception ) \ + do { \ + auto ex = exception; \ + if( fc::enable_record_assert_trip ) \ + fc::record_assert_trip( __FILE__, __LINE__, ex.what() ); \ + BOOST_THROW_EXCEPTION( ex ); \ + } while( false ) + namespace helpers { struct environment_extension_resources @@ -80,11 +91,9 @@ namespace helpers info->_item_count = index.size(); info->_item_sizeof = sizeof(typename IndexType::value_type); info->_item_additional_allocation = 0; -#ifndef ENABLE_MIRA size_t pureNodeSize = sizeof(typename IndexType::node_type) - sizeof(typename IndexType::value_type); info->_additional_container_allocation = info->_item_count*pureNodeSize; -#endif } template @@ -120,7 +129,7 @@ namespace chainbase { return less( a.c_str(), b.c_str() ); } -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR bool operator()( const shared_string& a, const std::string& b )const { return less( a.c_str(), b.c_str() ); @@ -131,6 +140,7 @@ namespace chainbase { return less( a.c_str(), b.c_str() ); } #endif + private: inline bool less( const char* a, const char* b )const { @@ -159,40 +169,29 @@ namespace chainbase { #define CHAINBASE_SET_INDEX_TYPE( OBJECT_TYPE, INDEX_TYPE ) \ namespace chainbase { template<> struct get_index_type { typedef INDEX_TYPE type; }; } - #ifdef ENABLE_MIRA - #define CHAINBASE_COPY_ACCESS public //MIRA is too weird to quench its thirst for object copies - #else - #define CHAINBASE_COPY_ACCESS private - #endif - #define CHAINBASE_OBJECT_1( object_class ) CHAINBASE_OBJECT_false( object_class ) #define CHAINBASE_OBJECT_2( object_class, allow_default ) CHAINBASE_OBJECT_##allow_default( object_class ) #define CHAINBASE_OBJECT_true( object_class ) CHAINBASE_OBJECT_COMMON( object_class ); public: object_class() : id(0) {} private: - #define CHAINBASE_OBJECT_COMMON( object_class ) \ - private: \ + #define CHAINBASE_OBJECT_COMMON( object_class ) \ + private: \ id_type id; \ - CHAINBASE_COPY_ACCESS: \ object_class( const object_class& source ) = default; \ /* problem with this being private? you most likely did \ - auto chain_object = db.get(...); \ - instead of \ - auto& chain_object_ref = db.get(...); \ - In case you actually need copy, use copy_chain_object() below \ + auto chain_object = db.get(...); \ + instead of \ + auto& chain_object_ref = db.get(...); \ + In case you actually need copy, use copy_chain_object() below \ */ \ object_class& operator= ( const object_class& source ) = default; \ - public: \ + public: \ id_type get_id() const { return id; } \ object_class( object_class&& source ) = default; \ object_class& operator= ( object_class&& source ) = default; \ object_class copy_chain_object() const { return *this; } \ friend class fc::reflector< object_class > - #ifdef ENABLE_MIRA - #define CHAINBASE_OBJECT_false( object_class ) CHAINBASE_OBJECT_true( object_class ) - #else #define CHAINBASE_OBJECT_false( object_class ) CHAINBASE_OBJECT_COMMON( object_class ); object_class() = delete; \ private: - #endif /** * use at the start of any class derived from chainbase::object<>, f.e.: @@ -205,17 +204,17 @@ namespace chainbase { #define CHAINBASE_ALLOCATED_MEMBERS( r, init, member ) , member( init ) - #define CHAINBASE_DEFAULT_CONSTRUCTOR( OBJECT_TYPE, ALLOCATED_MEMBERS... ) \ - template \ - OBJECT_TYPE( Allocator&& a, uint64_t _id, Constructor&& c ) \ + #define CHAINBASE_DEFAULT_CONSTRUCTOR( OBJECT_TYPE, ALLOCATED_MEMBERS... ) \ + template \ + OBJECT_TYPE( Allocator&& a, uint64_t _id, Constructor&& c ) \ : id( _id ) BOOST_PP_SEQ_FOR_EACH( CHAINBASE_ALLOCATED_MEMBERS, a, ALLOCATED_MEMBERS ) \ { c(*this); } - #define CHAINBASE_UNPACK_CONSTRUCTOR( OBJECT_TYPE, ALLOCATED_MEMBERS... ) \ - private: template \ - OBJECT_TYPE( Allocator&& a, uint64_t _id, std::function unpackFn) \ + #define CHAINBASE_UNPACK_CONSTRUCTOR( OBJECT_TYPE, ALLOCATED_MEMBERS... ) \ + private: template \ + OBJECT_TYPE( Allocator&& a, uint64_t _id, std::function unpackFn) \ : id( _id ) BOOST_PP_SEQ_FOR_EACH( CHAINBASE_ALLOCATED_MEMBERS, a, ALLOCATED_MEMBERS ) \ - { unpackFn(*this); } \ + { unpackFn(*this); } \ template friend class chainbase::generic_index @@ -287,24 +286,14 @@ namespace chainbase { typedef undo_state< value_type > undo_state_type; generic_index( allocator a, bfs::path p ) - :_stack(a),_indices( a, p ),_size_of_value_type( sizeof(typename MultiIndexType::value_type) ),_size_of_this(sizeof(*this)) - { -#ifdef ENABLE_MIRA - _revision = _indices.revision(); -#endif - } + :_stack(a),_indices( a, p ),_size_of_value_type( sizeof(typename MultiIndexType::value_type) ),_size_of_this(sizeof(*this)) {} generic_index( allocator a ) - :_stack(a),_indices( a ),_size_of_value_type( sizeof(typename MultiIndexType::value_type) ),_size_of_this(sizeof(*this)) - { -#ifdef ENABLE_MIRA - _revision = _indices.revision(); -#endif - } + :_stack(a),_indices( a ),_size_of_value_type( sizeof(typename MultiIndexType::value_type) ),_size_of_this(sizeof(*this)) {} void validate()const { if( sizeof(typename MultiIndexType::value_type) != _size_of_value_type || sizeof(*this) != _size_of_this ) - BOOST_THROW_EXCEPTION( std::runtime_error("content of memory does not match data expected by executable") ); + CHAINBASE_THROW_EXCEPTION( std::runtime_error("content of memory does not match data expected by executable") ); } /** @@ -318,13 +307,10 @@ namespace chainbase { auto insert_result = _indices.emplace( _indices.get_allocator(), new_id, std::forward( args )... ); if( !insert_result.second ) { - BOOST_THROW_EXCEPTION( std::logic_error("could not insert object, most likely a uniqueness constraint was violated") ); + CHAINBASE_THROW_EXCEPTION( std::logic_error("could not insert object, most likely a uniqueness constraint was violated") ); } ++_next_id; -#ifdef ENABLE_MIRA - _indices.set_next_id( _next_id ); -#endif on_create( *insert_result.first ); return *insert_result.first; } @@ -347,13 +333,11 @@ namespace chainbase { std::string msg = "could not insert unpacked object, most likely a uniqueness constraint was violated: `" + s + std::string("' conflicting object:`") + s2 + "'"; - BOOST_THROW_EXCEPTION(std::logic_error(msg)); + CHAINBASE_THROW_EXCEPTION(std::logic_error(msg)); } ++_next_id; -#ifdef ENABLE_MIRA - _indices.set_next_id(_next_id); -#endif + on_create(*insert_result.first); } @@ -362,7 +346,7 @@ namespace chainbase { on_modify( obj ); auto itr = _indices.iterator_to( obj ); auto ok = _indices.modify( itr, std::forward( m ) ); - if( !ok ) BOOST_THROW_EXCEPTION( std::logic_error( "Could not modify object, most likely a uniqueness constraint was violated" ) ); + if( !ok ) CHAINBASE_THROW_EXCEPTION( std::logic_error( "Could not modify object, most likely a uniqueness constraint was violated" ) ); } void remove( const value_type& obj ) { @@ -370,21 +354,29 @@ namespace chainbase { _indices.erase( _indices.iterator_to( obj ) ); } -#ifdef ENABLE_MIRA -//((bip::managed_mapped_file*)nullptr) - template< typename ByIndex, typename IterType > - IterType erase( IterType objI ) { - on_remove( *objI ); - return _indices.template mutable_get< ByIndex >().erase( objI ); - } -#else template< typename ByIndex > typename MultiIndexType::template index_iterator::type erase(typename MultiIndexType::template index_iterator::type objI) { auto& idx = _indices.template get< ByIndex >(); on_remove( *objI ); return idx.erase(objI); } -#endif + + template< typename ByIndex, typename ExternalStorageProcessor, typename Iterator = typename MultiIndexType::template index_iterator::type > + void move_to_external_storage(Iterator begin, Iterator end, ExternalStorageProcessor&& processor) + { + auto& idx = _indices.template get< ByIndex >(); + + for(auto objectI = begin; objectI != end;) + { + processor(*objectI); + + auto nextI = objectI; + ++nextI; + auto successor = idx.erase(objectI); + FC_ASSERT(successor == nextI); + objectI = successor; + } + } template const value_type* find( CompatibleKey&& key )const { @@ -396,7 +388,7 @@ namespace chainbase { template const value_type& get( CompatibleKey&& key )const { auto ptr = find( std::forward( key ) ); - if( !ptr ) BOOST_THROW_EXCEPTION( std::out_of_range("key not found") ); + if( !ptr ) CHAINBASE_THROW_EXCEPTION( std::out_of_range("key not found") ); return *ptr; } @@ -406,37 +398,6 @@ namespace chainbase { void clear() { _indices.clear(); } -#ifdef ENABLE_MIRA - void open( const bfs::path& p, const boost::any& o ) - { - _indices.open( p, o ); - _revision = _indices.revision(); - id_type next_id( 0 ); - if( _indices.get_metadata( "next_id", next_id ) ) - { - _next_id = next_id; - } - } - - void close() - { - _indices.put_metadata( "next_id", _next_id ); - _indices.close(); - } - - void wipe( const bfs::path& dir ) { _indices.wipe( dir ); } - - void flush() { _indices.flush(); } - - size_t get_cache_usage() const { return _indices.get_cache_usage(); } - - size_t get_cache_size() const { return _indices.get_cache_size(); } - - void dump_lb_call_counts() { _indices.dump_lb_call_counts(); } - - void trim_cache() { _indices.trim_cache(); } -#endif - class session { public: session( session&& mv ) @@ -482,10 +443,7 @@ namespace chainbase { session start_undo_session() { ++_revision; -#ifdef ENABLE_MIRA - _indices.set_revision( _revision ); - assert( _indices.revision() == _revision ); -#endif + _stack.emplace_back( _indices.get_allocator() ); _stack.back().old_next_id = _next_id; _stack.back().revision = _revision; @@ -519,7 +477,7 @@ namespace chainbase { ok = _indices.emplace( std::move( item.second ) ).second; } - if( !ok ) BOOST_THROW_EXCEPTION( std::logic_error( "Could not modify object, most likely a uniqueness constraint was violated" ) ); + if( !ok ) CHAINBASE_THROW_EXCEPTION( std::logic_error( "Could not modify object, most likely a uniqueness constraint was violated" ) ); } for( const auto& id : head.new_ids ) @@ -527,21 +485,14 @@ namespace chainbase { _indices.erase( _indices.find( id ) ); } _next_id = head.old_next_id; -#ifdef ENABLE_MIRA - _indices.set_next_id( _next_id ); -#endif for( auto& item : head.removed_values ) { bool ok = _indices.emplace( std::move( item.second ) ).second; - if( !ok ) BOOST_THROW_EXCEPTION( std::logic_error( "Could not restore object, most likely a uniqueness constraint was violated" ) ); + if( !ok ) CHAINBASE_THROW_EXCEPTION( std::logic_error( "Could not restore object, most likely a uniqueness constraint was violated" ) ); } _stack.pop_back(); --_revision; -#ifdef ENABLE_MIRA - _indices.set_revision( _revision ); - assert( _indices.revision() == _revision ); -#endif } /** @@ -650,10 +601,6 @@ namespace chainbase { _stack.pop_back(); --_revision; -#ifdef ENABLE_MIRA - _indices.set_revision( _revision ); - assert( _indices.revision() == _revision ); -#endif } /** @@ -678,12 +625,8 @@ namespace chainbase { void set_revision( int64_t revision ) { - if( _stack.size() != 0 ) BOOST_THROW_EXCEPTION( std::logic_error("cannot set revision while there is an existing undo stack") ); + if( _stack.size() != 0 ) CHAINBASE_THROW_EXCEPTION( std::logic_error("cannot set revision while there is an existing undo stack") ); _revision = revision; -#ifdef ENABLE_MIRA - _indices.set_revision( _revision ); - assert( _indices.revision() == _revision ); -#endif } private: @@ -804,18 +747,6 @@ namespace chainbase { virtual void dump_snapshot(snapshot_writer& writer) const = 0; virtual void load_snapshot(snapshot_reader& reader) = 0; -#ifdef ENABLE_MIRA - virtual void open( const bfs::path&, const boost::any& ) = 0; - virtual void close() = 0; - virtual void wipe( const bfs::path& dir ) = 0; - virtual void flush() = 0; - virtual size_t get_cache_usage() const = 0; - virtual size_t get_cache_size() const = 0; - virtual void dump_lb_call_counts() = 0; - virtual void trim_cache() = 0; - virtual void print_stats() const = 0; -#endif - void add_index_extension( std::shared_ptr< index_extension > ext ) { _extensions.push_back( ext ); } const index_extensions& get_index_extensions()const { return _extensions; } void* get()const { return _idx_ptr; } @@ -832,13 +763,6 @@ namespace chainbase { index_impl( BaseIndex& base ):abstract_index( &base ),_base(base){} -#ifdef ENABLE_MIRA - ~index_impl() - { - delete (BaseIndex*) abstract_index::_idx_ptr; - } -#endif - virtual unique_ptr start_undo_session() override { return unique_ptr(new session_impl( _base.start_undo_session() ) ); } @@ -881,54 +805,6 @@ namespace chainbase { loader.load(); } - -#ifdef ENABLE_MIRA - virtual void open( const bfs::path& p, const boost::any& o ) override final - { - _base.open( p, o ); - } - - virtual void close() override final - { - _base.close(); - } - - virtual void wipe( const bfs::path& dir ) override final - { - _base.wipe( dir ); - } - - virtual void flush() override final - { - _base.flush(); - } - - virtual size_t get_cache_usage() const override final - { - return _base.get_cache_usage(); - } - - virtual size_t get_cache_size() const override final - { - return _base.get_cache_size(); - } - - virtual void dump_lb_call_counts() override final - { - _base.dump_lb_call_counts(); - } - - virtual void trim_cache() override final - { - _base.trim_cache(); - } - - virtual void print_stats() const override final - { - _base.indicies().print_stats(); - } -#endif - private: BaseIndex& _base; }; @@ -974,13 +850,9 @@ namespace chainbase { void wipe_indexes(); public: - void open( const bfs::path& dir, uint32_t flags = 0, size_t shared_file_size = 0, const boost::any& database_cfg = nullptr, const helpers::environment_extension_resources* environment_extension = nullptr ); + void open( const bfs::path& dir, uint32_t flags = 0, size_t shared_file_size = 0, const boost::any& database_cfg = nullptr, const helpers::environment_extension_resources* environment_extension = nullptr, const bool wipe_shared_file = false ); void close(); void flush(); - size_t get_cache_usage() const; - size_t get_cache_size() const; - void dump_lb_call_counts(); - void trim_cache(); void wipe( const bfs::path& dir ); void resize( size_t new_shared_file_size ); void set_require_locking( bool enable_require_locking ); @@ -1067,13 +939,6 @@ namespace chainbase { for( const auto& i : _index_list ) i->set_revision( revision ); } -#ifdef ENABLE_MIRA - void print_stats() - { - for( const auto& i : _index_list ) i->print_stats(); - } -#endif - template void add_index() { @@ -1081,11 +946,10 @@ namespace chainbase { _index_types.back()->add_index( *this ); } -#ifndef ENABLE_MIRA auto get_segment_manager() -> decltype( ((bip::managed_mapped_file*)nullptr)->get_segment_manager()) { return _segment->get_segment_manager(); } -#endif + unsigned long long get_total_system_memory() const { #if !defined( __APPLE__ ) // OS X does not support _SC_AVPHYS_PAGES @@ -1099,11 +963,7 @@ namespace chainbase { size_t get_free_memory()const { -#ifdef ENABLE_MIRA - return get_total_system_memory(); -#else return _segment->get_segment_manager()->get_free_memory(); -#endif } size_t get_max_memory()const @@ -1129,7 +989,7 @@ namespace chainbase { if( !has_index< MultiIndexType >() ) { std::string type_name = boost::core::demangle( typeid( typename index_type::value_type ).name() ); - BOOST_THROW_EXCEPTION( std::runtime_error( "unable to find index for " + type_name + " in database" ) ); + CHAINBASE_THROW_EXCEPTION( std::runtime_error( "unable to find index for " + type_name + " in database" ) ); } return *index_type_ptr( _index_map[index_type::value_type::type_id]->get() ); @@ -1143,7 +1003,7 @@ namespace chainbase { if( !has_index< MultiIndexType >() ) { std::string type_name = boost::core::demangle( typeid( typename index_type::value_type ).name() ); - BOOST_THROW_EXCEPTION( std::runtime_error( "unable to find index for " + type_name + " in database" ) ); + CHAINBASE_THROW_EXCEPTION( std::runtime_error( "unable to find index for " + type_name + " in database" ) ); } _index_map[index_type::value_type::type_id]->add_index_extension( ext ); @@ -1159,7 +1019,7 @@ namespace chainbase { if( !has_index< MultiIndexType >() ) { std::string type_name = boost::core::demangle( typeid( typename index_type::value_type ).name() ); - BOOST_THROW_EXCEPTION( std::runtime_error( "unable to find index for " + type_name + " in database" ) ); + CHAINBASE_THROW_EXCEPTION( std::runtime_error( "unable to find index for " + type_name + " in database" ) ); } return index_type_ptr( _index_map[index_type::value_type::type_id]->get() )->indicies().template get(); @@ -1175,7 +1035,7 @@ namespace chainbase { if( !has_index< MultiIndexType >() ) { std::string type_name = boost::core::demangle( typeid( typename index_type::value_type ).name() ); - BOOST_THROW_EXCEPTION( std::runtime_error( "unable to find index for " + type_name + " in database" ) ); + CHAINBASE_THROW_EXCEPTION( std::runtime_error( "unable to find index for " + type_name + " in database" ) ); } return *index_type_ptr( _index_map[index_type::value_type::type_id]->get() ); @@ -1210,7 +1070,7 @@ namespace chainbase { auto obj = find< ObjectType, IndexedByType >( std::forward< CompatibleKey >( key ) ); if( !obj ) { - BOOST_THROW_EXCEPTION( std::out_of_range( "unknown key" ) ); + CHAINBASE_THROW_EXCEPTION( std::out_of_range( "unknown key" ) ); } return *obj; } @@ -1220,7 +1080,7 @@ namespace chainbase { { CHAINBASE_REQUIRE_READ_LOCK("get", ObjectType); auto obj = find< ObjectType >( key ); - if( !obj ) BOOST_THROW_EXCEPTION( std::out_of_range( "unknown key") ); + if( !obj ) CHAINBASE_THROW_EXCEPTION( std::out_of_range( "unknown key") ); return *obj; } @@ -1258,7 +1118,7 @@ namespace chainbase { template< typename Lambda > auto with_read_lock( Lambda&& callback, uint64_t wait_micro = 1000000 ) -> decltype( (*(Lambda*)nullptr)() ) { -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR read_lock lock( _rw_lock, bip::defer_lock_type() ); #else read_lock lock( _rw_lock, boost::defer_lock_t() ); @@ -1276,7 +1136,7 @@ namespace chainbase { else { if( !lock.timed_lock( boost::posix_time::microsec_clock::universal_time() + boost::posix_time::microseconds( wait_micro ) ) ) - BOOST_THROW_EXCEPTION( lock_exception() ); + CHAINBASE_THROW_EXCEPTION( lock_exception() ); } return callback(); @@ -1316,6 +1176,10 @@ namespace chainbase { const abstract_index_cntr_t& get_abstract_index_cntr() const { return _index_list; } + protected: + bool get_is_open() const + { return _is_open; } + private: template void add_index_helper() { @@ -1326,15 +1190,16 @@ namespace chainbase { std::string type_name = boost::core::demangle( typeid( typename index_type::value_type ).name() ); if( !( _index_map.size() <= type_id || _index_map[ type_id ] == nullptr ) ) { - BOOST_THROW_EXCEPTION( std::logic_error( type_name + "::type_id is already in use" ) ); + CHAINBASE_THROW_EXCEPTION( std::logic_error( type_name + "::type_id is already in use" ) ); } - index_type* idx_ptr = nullptr; -#ifdef ENABLE_MIRA +#ifdef ENABLE_STD_ALLOCATOR idx_ptr = new index_type( index_alloc() ); #else idx_ptr = _segment->find_or_construct< index_type >( type_name.c_str() )( index_alloc( _segment->get_segment_manager() ) ); #endif + + idx_ptr->validate(); if( type_id >= _index_map.size() ) @@ -1344,18 +1209,13 @@ namespace chainbase { _index_map[ type_id ].reset( new_index ); _index_list.push_back( new_index ); - -#ifdef ENABLE_MIRA - if( _is_open ) new_index->open( _data_dir, _database_cfg ); -#endif } read_write_mutex _rw_lock; -#ifndef ENABLE_MIRA + unique_ptr _segment; unique_ptr _meta; bip::file_lock _flock; -#endif /** * This is a sparse list of known indicies kept to accelerate creation of undo sessions diff --git a/libraries/chainbase/include/chainbase/state_snapshot_support.hpp b/libraries/chainbase/include/chainbase/state_snapshot_support.hpp index 0e124dc1ef..c4c5437923 100644 --- a/libraries/chainbase/include/chainbase/state_snapshot_support.hpp +++ b/libraries/chainbase/include/chainbase/state_snapshot_support.hpp @@ -13,12 +13,6 @@ #include #include -namespace mira -{ -template< typename Arg1, typename Arg2, typename Arg3 > -struct multi_index_adapter; -} /// namespace mira - namespace chainbase { @@ -286,19 +280,8 @@ class generic_index_snapshot_dumper final : public generic_index_serialize_base } private: - template - struct IteratorProvider - { - typedef typename ContainerType::iterator iterator; - }; - - template< typename Arg1, typename Arg2, typename Arg3 > - struct IteratorProvider > - { - typedef typename mira::multi_index_adapter< Arg1, Arg2, Arg3 >::iter_type iterator; - }; - typedef typename IteratorProvider::iterator iterator; + typedef typename MultiIndexType::iterator iterator; const MultiIndexType& _data_source; iterator _start; iterator _end; diff --git a/libraries/chainbase/include/chainbase/util/object_id.hpp b/libraries/chainbase/include/chainbase/util/object_id.hpp index d2961fc051..20a64583cc 100644 --- a/libraries/chainbase/include/chainbase/util/object_id.hpp +++ b/libraries/chainbase/include/chainbase/util/object_id.hpp @@ -23,11 +23,7 @@ class oid __id_type _id; public: -#ifdef ENABLE_MIRA - oid( __id_type i = 0 ) : _id( i ) {} -#else - explicit oid( __id_type i ) : _id( i ) {} //lack of default outside of MIRA makes sure all chain-object constructors fill their id members -#endif + explicit oid( __id_type i ) : _id( i ) {} //lack of default makes sure all chain-object constructors fill their id members oid& operator++() { ++_id; return *this; } diff --git a/libraries/chainbase/include/chainbase/util/object_id_serialization.hpp b/libraries/chainbase/include/chainbase/util/object_id_serialization.hpp index cf624f4776..c7c3e499b9 100644 --- a/libraries/chainbase/include/chainbase/util/object_id_serialization.hpp +++ b/libraries/chainbase/include/chainbase/util/object_id_serialization.hpp @@ -5,7 +5,7 @@ #include namespace fc -{ +{ class variant; template struct get_typename; diff --git a/libraries/chainbase/src/chainbase.cpp b/libraries/chainbase/src/chainbase.cpp index dc92f1c772..271abce71f 100644 --- a/libraries/chainbase/src/chainbase.cpp +++ b/libraries/chainbase/src/chainbase.cpp @@ -14,14 +14,13 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache public: -#ifdef ENABLE_MIRA +#ifdef ENABLE_STD_ALLOCATOR environment_check() #else template< typename Allocator > environment_check( allocator< Allocator > a ) : version_info( a ), plugins( a ) #endif - { memset( &compiler_version, 0, sizeof( compiler_version ) ); memcpy( &compiler_version, __VERSION__, std::min( strlen(__VERSION__), 256 ) ); @@ -44,7 +43,7 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache environment_check& operator = ( const environment_check& other ) { -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR plugins = plugins; #endif compiler_version = other.compiler_version; @@ -63,18 +62,15 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache retVal += ", \"apple\":" + std::to_string(apple); retVal += ", \"windows\":" + std::to_string(windows); -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR retVal += ", " + std::string( version_info.c_str() ); retVal += ", " + dump( plugins ); #endif - retVal += "}"; return retVal; } - -#ifndef ENABLE_MIRA - +#ifndef ENABLE_STD_ALLOCATOR template< typename Set > std::string dump( const Set& source ) const { @@ -136,7 +132,6 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache shared_string version_info; t_flat_set< shared_string > plugins; #endif - boost::array compiler_version; bool debug = false; bool apple = false; @@ -145,18 +140,18 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache bool created_storage = true; }; - void database::open( const bfs::path& dir, uint32_t flags, size_t shared_file_size, const boost::any& database_cfg, const helpers::environment_extension_resources* environment_extension ) + void database::open( const bfs::path& dir, uint32_t flags, size_t shared_file_size, const boost::any& database_cfg, const helpers::environment_extension_resources* environment_extension, const bool wipe_shared_file ) { assert( dir.is_absolute() ); bfs::create_directories( dir ); if( _data_dir != dir ) close(); + if( wipe_shared_file ) wipe( dir ); _data_dir = dir; _database_cfg = database_cfg; - -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR auto abs_path = bfs::absolute( dir / "shared_memory.bin" ); - + if( bfs::exists( abs_path ) ) { _file_size = bfs::file_size( abs_path ); @@ -214,97 +209,28 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache _flock = bip::file_lock( abs_path.generic_string().c_str() ); if( !_flock.try_lock() ) BOOST_THROW_EXCEPTION( std::runtime_error( "could not gain write access to the shared memory file" ) ); -#else - for( auto& item : _index_list ) - { - item->open( _data_dir, _database_cfg ); - } #endif + _is_open = true; } void database::flush() { -#ifndef ENABLE_MIRA if( _segment ) _segment->flush(); if( _meta ) _meta->flush(); -#else - for( auto& item : _index_list ) - { - item->flush(); - } -#endif - } - - size_t database::get_cache_usage() const - { -#ifdef ENABLE_MIRA - size_t cache_size = 0; - for( const auto& i : _index_list ) - { - cache_size += i->get_cache_usage(); - } - return cache_size; -#else - return 0; -#endif - } - - size_t database::get_cache_size() const - { -#ifdef ENABLE_MIRA - size_t cache_size = 0; - for( const auto& i : _index_list ) - { - cache_size += i->get_cache_size(); - } - return cache_size; -#else - return 0; -#endif - } - - void database::dump_lb_call_counts() - { -#ifdef ENABLE_MIRA - for( const auto& i : _index_list ) - { - i->dump_lb_call_counts(); - } -#endif - } - - void database::trim_cache() - { -#ifdef ENABLE_MIRA - if( _index_list.size() ) - { - (*_index_list.begin())->trim_cache(); - } -#endif } void database::close() { if( _is_open ) { -#ifndef ENABLE_MIRA _segment.reset(); _meta.reset(); _data_dir = bfs::path(); wipe_indexes(); -#else - undo_all(); - - for( auto& item : _index_list ) - { - item->close(); - } - wipe_indexes(); -#endif _is_open = false; } } @@ -318,7 +244,6 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache void database::wipe( const bfs::path& dir ) { assert( !_is_open ); -#ifndef ENABLE_MIRA _segment.reset(); _meta.reset(); bfs::remove_all( dir / "shared_memory.bin" ); @@ -327,21 +252,10 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache wipe_indexes(); -#else - for( auto& item : _index_list ) - { - item->wipe( dir ); - } - - wipe_indexes(); - - _index_types.clear(); -#endif } void database::resize( size_t new_shared_file_size ) { -#ifndef ENABLE_MIRA if( _undo_session_count ) BOOST_THROW_EXCEPTION( std::runtime_error( "Cannot resize shared memory file while undo session is active" ) ); @@ -356,7 +270,6 @@ size_t snapshot_base_serializer::worker_common_base::get_serialized_object_cache { index_type->add_index( *this ); } -#endif } void database::set_require_locking( bool enable_require_locking ) diff --git a/libraries/chainbase/test/CMakeLists.txt b/libraries/chainbase/test/CMakeLists.txt index 0e2eb2f7b7..22df246c8b 100644 --- a/libraries/chainbase/test/CMakeLists.txt +++ b/libraries/chainbase/test/CMakeLists.txt @@ -1,4 +1,5 @@ file(GLOB UNIT_TESTS "*.cpp") add_executable( chainbase_test ${UNIT_TESTS} ) -target_link_libraries( chainbase_test chainbase mira ${PLATFORM_SPECIFIC_LIBS} ) +target_link_libraries( chainbase_test chainbase + ${PLATFORM_SPECIFIC_LIBS} ) diff --git a/libraries/chainbase/test/test.cpp b/libraries/chainbase/test/test.cpp index a328c56c75..65a40400df 100644 --- a/libraries/chainbase/test/test.cpp +++ b/libraries/chainbase/test/test.cpp @@ -17,8 +17,6 @@ using namespace boost::multi_index; //BOOST_TEST_SUITE( serialization_tests, clean_database_fixture ) -#ifndef ENABLE_MIRA - class book : public chainbase::object<0, book> { CHAINBASE_OBJECT( book ); @@ -155,5 +153,4 @@ BOOST_AUTO_TEST_CASE( open_and_create ) { } } -// BOOST_AUTO_TEST_SUITE_END() -#endif +// BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/libraries/fc/CMakeLists.txt b/libraries/fc/CMakeLists.txt index d8e3341096..6f8015bf58 100644 --- a/libraries/fc/CMakeLists.txt +++ b/libraries/fc/CMakeLists.txt @@ -129,6 +129,7 @@ ELSE(WIN32) LIST(APPEND BOOST_COMPONENTS coroutine) + cmake_policy(SET CMP0057 NEW) FIND_PACKAGE(Boost 1.53 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a;.so") @@ -259,15 +260,18 @@ setup_library( fc SOURCES ${sources} LIBRARY_TYPE STATIC DONT_INSTALL_LIBRARY ) # begin readline stuff find_package(Curses) + +if( HIVE_STATIC_BUILD ) + set( Readline_USE_STATIC_LIBS ON CACHE BOOL "Use static readline libraries" ) +endif() find_package(Readline) file(GLOB HEADERS "include/bts/cli/*.hpp") if (READLINE_FOUND) target_compile_definitions (fc PRIVATE HAVE_READLINE) - set(readline_libraries ${Readline_LIBRARY}) if (CURSES_FOUND) - list(APPEND readline_libraries ${CURSES_LIBRARY}) + list(APPEND Readline_LIBRARIES ${CURSES_LIBRARY}) endif() set(readline_includes ${Readline_INCLUDE_DIR}) endif() @@ -276,10 +280,6 @@ if(WIN32) endif(WIN32) # end readline stuff -if( NOT CPP_STANDARD ) - set( CPP_STANDARD, "-std=c++11" ) -endif() - IF(WIN32) target_compile_definitions(fc PUBLIC WIN32 NOMINMAX _WIN32_WINNT=0x0501 _CRT_SECURE_NO_WARNINGS _SCL_SERCURE_NO_WARNINGS @@ -298,15 +298,15 @@ IF(WIN32) # Put MinGW specific compiler settings here endif() ELSE() - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") IF(APPLE) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CPP_STANDARD} -stdlib=libc++ -Wall") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -Wall") ELSE() if( NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" ) - target_compile_options(fc PUBLIC ${CPP_STANDARD} -Wall -fnon-call-exceptions) + target_compile_options(fc PUBLIC -Wall -fnon-call-exceptions) endif() - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CPP_STANDARD} -Wall -fnon-call-exceptions") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fnon-call-exceptions") ENDIF() ENDIF() @@ -360,7 +360,6 @@ target_include_directories(fc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${Boost_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR} - "vendor/diff-match-patch-cpp-stl" ${CMAKE_CURRENT_SOURCE_DIR}/vendor/websocketpp "${readline_includes}" @@ -375,7 +374,8 @@ target_include_directories(fc IF(NOT WIN32) set(LINK_USR_LOCAL_LIB -L/usr/local/lib) ENDIF() -target_link_libraries( fc PUBLIC ${LINK_USR_LOCAL_LIB} equihash ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES} ${Boost_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${readline_libraries} ${ECC_LIB} ) + +target_link_libraries( fc PUBLIC ${LINK_USR_LOCAL_LIB} equihash ${OPENSSL_LIBRARIES} ${ZLIB_LIBRARIES} ${BZIP2_LIBRARIES} ${Boost_LIBRARIES} ${PLATFORM_SPECIFIC_LIBS} ${RPCRT4} ${CMAKE_DL_LIBS} ${rt_library} ${Readline_LIBRARIES} ${ECC_LIB} ) if(MSVC) set_source_files_properties( src/network/http/websocket.cpp PROPERTIES COMPILE_FLAGS "/bigobj" ) @@ -386,7 +386,7 @@ IF(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY MATCHES "\\.(a|lib)$") IF(MSVC) add_definitions(/DBOOST_TEST_DYN_LINK) ELSE(MSVC) -add_definitions(-DBOOST_TEST_DYN_LINK) +# add_definitions(-DBOOST_TEST_DYN_LINK) ENDIF(MSVC) ENDIF() diff --git a/libraries/fc/CMakeModules/FindBoost.cmake b/libraries/fc/CMakeModules/FindBoost.cmake deleted file mode 100644 index 4c37f5fff9..0000000000 --- a/libraries/fc/CMakeModules/FindBoost.cmake +++ /dev/null @@ -1,1180 +0,0 @@ -# - Find Boost include dirs and libraries -# Use this module by invoking find_package with the form: -# find_package(Boost -# [version] [EXACT] # Minimum or EXACT version e.g. 1.36.0 -# [REQUIRED] # Fail with error if Boost is not found -# [COMPONENTS ...] # Boost libraries by their canonical name -# ) # e.g. "date_time" for "libboost_date_time" -# This module finds headers and requested component libraries OR a CMake -# package configuration file provided by a "Boost CMake" build. For the -# latter case skip to the "Boost CMake" section below. For the former -# case results are reported in variables: -# Boost_FOUND - True if headers and requested libraries were found -# Boost_INCLUDE_DIRS - Boost include directories -# Boost_LIBRARY_DIRS - Link directories for Boost libraries -# Boost_LIBRARIES - Boost component libraries to be linked -# Boost__FOUND - True if component was found ( is upper-case) -# Boost__LIBRARY - Libraries to link for component (may include -# target_link_libraries debug/optimized keywords) -# Boost_VERSION - BOOST_VERSION value from boost/version.hpp -# Boost_LIB_VERSION - Version string appended to library filenames -# Boost_MAJOR_VERSION - Boost major version number (X in X.y.z) -# Boost_MINOR_VERSION - Boost minor version number (Y in x.Y.z) -# Boost_SUBMINOR_VERSION - Boost subminor version number (Z in x.y.Z) -# Boost_LIB_DIAGNOSTIC_DEFINITIONS (Windows) -# - Pass to add_definitions() to have diagnostic -# information about Boost's automatic linking -# displayed during compilation -# -# This module reads hints about search locations from variables: -# BOOST_ROOT - Preferred installation prefix -# (or BOOSTROOT) -# BOOST_INCLUDEDIR - Preferred include directory e.g. /include -# BOOST_LIBRARYDIR - Preferred library directory e.g. /lib -# Boost_NO_SYSTEM_PATHS - Set to ON to disable searching in locations not -# specified by these hint variables. Default is OFF. -# Boost_ADDITIONAL_VERSIONS -# - List of Boost versions not known to this module -# (Boost install locations may contain the version) -# and saves search results persistently in CMake cache entries: -# Boost_INCLUDE_DIR - Directory containing Boost headers -# Boost_LIBRARY_DIR - Directory containing Boost libraries -# Boost__LIBRARY_DEBUG - Component library debug variant -# Boost__LIBRARY_RELEASE - Component library release variant -# Users may set the these hints or results as cache entries. Projects should -# not read these entries directly but instead use the above result variables. -# Note that some hint names start in upper-case "BOOST". One may specify -# these as environment variables if they are not specified as CMake variables -# or cache entries. -# -# This module first searches for the Boost header files using the above hint -# variables (excluding BOOST_LIBRARYDIR) and saves the result in -# Boost_INCLUDE_DIR. Then it searches for requested component libraries using -# the above hints (excluding BOOST_INCLUDEDIR and Boost_ADDITIONAL_VERSIONS), -# "lib" directories near Boost_INCLUDE_DIR, and the library name configuration -# settings below. It saves the library directory in Boost_LIBRARY_DIR and -# individual library locations in Boost__LIBRARY_DEBUG and -# Boost__LIBRARY_RELEASE. When one changes settings used by previous -# searches in the same build tree (excluding environment variables) this -# module discards previous search results affected by the changes and searches -# again. -# -# Boost libraries come in many variants encoded in their file name. Users or -# projects may tell this module which variant to find by setting variables: -# Boost_USE_MULTITHREADED - Set to OFF to use the non-multithreaded -# libraries ('mt' tag). Default is ON. -# Boost_USE_STATIC_LIBS - Set to ON to force the use of the static -# libraries. Default is OFF. -# Boost_USE_STATIC_RUNTIME - Set to ON or OFF to specify whether to use -# libraries linked statically to the C++ runtime -# ('s' tag). Default is platform dependent. -# Boost_USE_DEBUG_PYTHON - Set to ON to use libraries compiled with a -# debug Python build ('y' tag). Default is OFF. -# Boost_USE_STLPORT - Set to ON to use libraries compiled with -# STLPort ('p' tag). Default is OFF. -# Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS -# - Set to ON to use libraries compiled with -# STLPort deprecated "native iostreams" -# ('n' tag). Default is OFF. -# Boost_COMPILER - Set to the compiler-specific library suffix -# (e.g. "-gcc43"). Default is auto-computed -# for the C++ compiler in use. -# Boost_THREADAPI - Suffix for "thread" component library name, -# such as "pthread" or "win32". Names with -# and without this suffix will both be tried. -# Other variables one may set to control this module are: -# Boost_DEBUG - Set to ON to enable debug output from FindBoost. -# Please enable this before filing any bug report. -# Boost_DETAILED_FAILURE_MSG -# - Set to ON to add detailed information to the -# failure message even when the REQUIRED option -# is not given to the find_package call. -# Boost_REALPATH - Set to ON to resolve symlinks for discovered -# libraries to assist with packaging. For example, -# the "system" component library may be resolved to -# "/usr/lib/libboost_system.so.1.42.0" instead of -# "/usr/lib/libboost_system.so". This does not -# affect linking and should not be enabled unless -# the user needs this information. -# On Visual Studio and Borland compilers Boost headers request automatic -# linking to corresponding libraries. This requires matching libraries to be -# linked explicitly or available in the link library search path. In this -# case setting Boost_USE_STATIC_LIBS to OFF may not achieve dynamic linking. -# Boost automatic linking typically requests static libraries with a few -# exceptions (such as Boost.Python). Use -# add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS}) -# to ask Boost to report information about automatic linking requests. -# -# Example to find Boost headers only: -# find_package(Boost 1.36.0) -# if(Boost_FOUND) -# include_directories(${Boost_INCLUDE_DIRS}) -# add_executable(foo foo.cc) -# endif() -# Example to find Boost headers and some libraries: -# set(Boost_USE_STATIC_LIBS ON) -# set(Boost_USE_MULTITHREADED ON) -# set(Boost_USE_STATIC_RUNTIME OFF) -# find_package(Boost 1.36.0 COMPONENTS date_time filesystem system ...) -# if(Boost_FOUND) -# include_directories(${Boost_INCLUDE_DIRS}) -# add_executable(foo foo.cc) -# target_link_libraries(foo ${Boost_LIBRARIES}) -# endif() -# -# Boost CMake ---------------------------------------------------------- -# -# If Boost was built using the boost-cmake project it provides a package -# configuration file for use with find_package's Config mode. This module -# looks for the package configuration file called BoostConfig.cmake or -# boost-config.cmake and stores the result in cache entry "Boost_DIR". If -# found, the package configuration file is loaded and this module returns with -# no further action. See documentation of the Boost CMake package -# configuration for details on what it provides. -# -# Set Boost_NO_BOOST_CMAKE to ON to disable the search for boost-cmake. - -#============================================================================= -# Copyright 2006-2012 Kitware, Inc. -# Copyright 2006-2008 Andreas Schneider -# Copyright 2007 Wengo -# Copyright 2007 Mike Jackson -# Copyright 2008 Andreas Pakulat -# Copyright 2008-2012 Philip Lowman -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - - -#------------------------------------------------------------------------------- -# Before we go searching, check whether boost-cmake is avaialble, unless the -# user specifically asked NOT to search for boost-cmake. -# -# If Boost_DIR is set, this behaves as any find_package call would. If not, -# it looks at BOOST_ROOT and BOOSTROOT to find Boost. -# - -message(STATUS "Using custom FindBoost.cmake") - -if (NOT Boost_NO_BOOST_CMAKE) - # If Boost_DIR is not set, look for BOOSTROOT and BOOST_ROOT as alternatives, - # since these are more conventional for Boost. - if ("$ENV{Boost_DIR}" STREQUAL "") - if (NOT "$ENV{BOOST_ROOT}" STREQUAL "") - set(ENV{Boost_DIR} $ENV{BOOST_ROOT}) - elseif (NOT "$ENV{BOOSTROOT}" STREQUAL "") - set(ENV{Boost_DIR} $ENV{BOOSTROOT}) - endif() - endif() - - # Do the same find_package call but look specifically for the CMake version. - # Note that args are passed in the Boost_FIND_xxxxx variables, so there is no - # need to delegate them to this find_package call. - find_package(Boost QUIET NO_MODULE) - mark_as_advanced(Boost_DIR) - - # If we found boost-cmake, then we're done. Print out what we found. - # Otherwise let the rest of the module try to find it. - if (Boost_FOUND) - message("Boost ${Boost_FIND_VERSION} found.") - if (Boost_FIND_COMPONENTS) - message("Found Boost components:") - message(" ${Boost_FIND_COMPONENTS}") - endif() - return() - endif() -endif() - - -#------------------------------------------------------------------------------- -# FindBoost functions & macros -# - -############################################ -# -# Check the existence of the libraries. -# -############################################ -# This macro was taken directly from the FindQt4.cmake file that is included -# with the CMake distribution. This is NOT my work. All work was done by the -# original authors of the FindQt4.cmake file. Only minor modifications were -# made to remove references to Qt and make this file more generally applicable -# And ELSE/ENDIF pairs were removed for readability. -######################################################################### - -macro(_Boost_ADJUST_LIB_VARS basename) - if(Boost_INCLUDE_DIR ) - if(Boost_${basename}_LIBRARY_DEBUG AND Boost_${basename}_LIBRARY_RELEASE) - # if the generator supports configuration types then set - # optimized and debug libraries, or if the CMAKE_BUILD_TYPE has a value - if(CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE) - set(Boost_${basename}_LIBRARY optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG}) - else() - # if there are no configuration types and CMAKE_BUILD_TYPE has no value - # then just use the release libraries - set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} ) - endif() - # FIXME: This probably should be set for both cases - set(Boost_${basename}_LIBRARIES optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG}) - endif() - - # if only the release version was found, set the debug variable also to the release version - if(Boost_${basename}_LIBRARY_RELEASE AND NOT Boost_${basename}_LIBRARY_DEBUG) - set(Boost_${basename}_LIBRARY_DEBUG ${Boost_${basename}_LIBRARY_RELEASE}) - set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE}) - set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE}) - endif() - - # if only the debug version was found, set the release variable also to the debug version - if(Boost_${basename}_LIBRARY_DEBUG AND NOT Boost_${basename}_LIBRARY_RELEASE) - set(Boost_${basename}_LIBRARY_RELEASE ${Boost_${basename}_LIBRARY_DEBUG}) - set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_DEBUG}) - set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_DEBUG}) - endif() - - # If the debug & release library ends up being the same, omit the keywords - if(${Boost_${basename}_LIBRARY_RELEASE} STREQUAL ${Boost_${basename}_LIBRARY_DEBUG}) - set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} ) - set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE} ) - endif() - - if(Boost_${basename}_LIBRARY) - set(Boost_${basename}_FOUND ON) - endif() - - endif() - # Make variables changeble to the advanced user - mark_as_advanced( - Boost_${basename}_LIBRARY_RELEASE - Boost_${basename}_LIBRARY_DEBUG - ) -endmacro() - -macro(_Boost_CHANGE_DETECT changed_var) - set(${changed_var} 0) - foreach(v ${ARGN}) - if(DEFINED _Boost_COMPONENTS_SEARCHED) - if(${v}) - if(_${v}_LAST) - string(COMPARE NOTEQUAL "${${v}}" "${_${v}_LAST}" _${v}_CHANGED) - else() - set(_${v}_CHANGED 1) - endif() - elseif(_${v}_LAST) - set(_${v}_CHANGED 1) - endif() - if(_${v}_CHANGED) - set(${changed_var} 1) - endif() - else() - set(_${v}_CHANGED 0) - endif() - endforeach() -endmacro() - -macro(_Boost_FIND_LIBRARY var) - find_library(${var} ${ARGN}) - - # If we found the first library save Boost_LIBRARY_DIR. - if(${var} AND NOT Boost_LIBRARY_DIR) - get_filename_component(_dir "${${var}}" PATH) - set(Boost_LIBRARY_DIR "${_dir}" CACHE PATH "Boost library directory" FORCE) - endif() - - # If Boost_LIBRARY_DIR is known then search only there. - if(Boost_LIBRARY_DIR) - set(_boost_LIBRARY_SEARCH_DIRS ${Boost_LIBRARY_DIR} NO_DEFAULT_PATH) - endif() -endmacro() - -#------------------------------------------------------------------------------- - -# -# Runs compiler with "-dumpversion" and parses major/minor -# version with a regex. -# -function(_Boost_COMPILER_DUMPVERSION _OUTPUT_VERSION) - - exec_program(${CMAKE_CXX_COMPILER} - ARGS ${CMAKE_CXX_COMPILER_ARG1} -dumpversion - OUTPUT_VARIABLE _boost_COMPILER_VERSION - ) - string(REGEX REPLACE "([0-9])\\.([0-9])(\\.[0-9])?" "\\1\\2" - _boost_COMPILER_VERSION ${_boost_COMPILER_VERSION}) - - set(${_OUTPUT_VERSION} ${_boost_COMPILER_VERSION} PARENT_SCOPE) -endfunction() - -# -# Take a list of libraries with "thread" in it -# and prepend duplicates with "thread_${Boost_THREADAPI}" -# at the front of the list -# -function(_Boost_PREPEND_LIST_WITH_THREADAPI _output) - set(_orig_libnames ${ARGN}) - string(REPLACE "thread" "thread_${Boost_THREADAPI}" _threadapi_libnames "${_orig_libnames}") - set(${_output} ${_threadapi_libnames} ${_orig_libnames} PARENT_SCOPE) -endfunction() - -# -# If a library is found, replace its cache entry with its REALPATH -# -function(_Boost_SWAP_WITH_REALPATH _library _docstring) - if(${_library}) - get_filename_component(_boost_filepathreal ${${_library}} REALPATH) - unset(${_library} CACHE) - set(${_library} ${_boost_filepathreal} CACHE FILEPATH "${_docstring}") - endif() -endfunction() - -function(_Boost_CHECK_SPELLING _var) - if(${_var}) - string(TOUPPER ${_var} _var_UC) - message(FATAL_ERROR "ERROR: ${_var} is not the correct spelling. The proper spelling is ${_var_UC}.") - endif() -endfunction() - -# Guesses Boost's compiler prefix used in built library names -# Returns the guess by setting the variable pointed to by _ret -function(_Boost_GUESS_COMPILER_PREFIX _ret) - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" - OR "${CMAKE_CXX_COMPILER}" MATCHES "icl" - OR "${CMAKE_CXX_COMPILER}" MATCHES "icpc") - if(WIN32) - set (_boost_COMPILER "-iw") - else() - set (_boost_COMPILER "-il") - endif() - elseif (MSVC12) - set(_boost_COMPILER "-vc120") - elseif (MSVC11) - set(_boost_COMPILER "-vc110") - elseif (MSVC10) - set(_boost_COMPILER "-vc100") - elseif (MSVC90) - set(_boost_COMPILER "-vc90") - elseif (MSVC80) - set(_boost_COMPILER "-vc80") - elseif (MSVC71) - set(_boost_COMPILER "-vc71") - elseif (MSVC70) # Good luck! - set(_boost_COMPILER "-vc7") # yes, this is correct - elseif (MSVC60) # Good luck! - set(_boost_COMPILER "-vc6") # yes, this is correct - elseif (BORLAND) - set(_boost_COMPILER "-bcb") - elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "SunPro") - set(_boost_COMPILER "-sw") - elseif (MINGW) - if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) - set(_boost_COMPILER "-mgw") # no GCC version encoding prior to 1.34 - else() - _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION) - set(_boost_COMPILER "-mgw${_boost_COMPILER_VERSION}") - endif() - elseif (UNIX) - if (CMAKE_COMPILER_IS_GNUCXX) - if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) - set(_boost_COMPILER "-gcc") # no GCC version encoding prior to 1.34 - else() - _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION) - # Determine which version of GCC we have. - if(APPLE) - if(Boost_MINOR_VERSION) - if(${Boost_MINOR_VERSION} GREATER 35) - # In Boost 1.36.0 and newer, the mangled compiler name used - # on Mac OS X/Darwin is "xgcc". - set(_boost_COMPILER "-xgcc${_boost_COMPILER_VERSION}") - else() - # In Boost <= 1.35.0, there is no mangled compiler name for - # the Mac OS X/Darwin version of GCC. - set(_boost_COMPILER "") - endif() - else() - # We don't know the Boost version, so assume it's - # pre-1.36.0. - set(_boost_COMPILER "") - endif() - else() - set(_boost_COMPILER "-gcc${_boost_COMPILER_VERSION}") - endif() - endif() - endif () - else() - # TODO at least Boost_DEBUG here? - set(_boost_COMPILER "") - endif() - set(${_ret} ${_boost_COMPILER} PARENT_SCOPE) -endfunction() - -function(_Boost_consider_adding_pthreads _outvar) - # On Unix platforms (excluding cygwin) add pthreads to Boost_LIBRARIES - # if the user is searching for the boost-thread component. - if(UNIX AND NOT CYGWIN) - list(FIND Boost_FIND_COMPONENTS thread _using_boost_thread) - if(_using_boost_thread GREATER -1) - find_library(BOOST_THREAD_LIBRARY NAMES pthread - DOC "The threading library used by boost-thread" - ) - if(BOOST_THREAD_LIBRARY) - set(${_outvar} ${ARGN} ${BOOST_THREAD_LIBRARY} PARENT_SCOPE) - endif() - endif() - endif() -endfunction() - -# -# End functions/macros -# -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# main. -#------------------------------------------------------------------------------- - -if(NOT DEFINED Boost_USE_MULTITHREADED) - set(Boost_USE_MULTITHREADED TRUE) -endif() - -# Check the version of Boost against the requested version. -if(Boost_FIND_VERSION AND NOT Boost_FIND_VERSION_MINOR) - message(SEND_ERROR "When requesting a specific version of Boost, you must provide at least the major and minor version numbers, e.g., 1.34") -endif() - -if(Boost_FIND_VERSION_EXACT) - # The version may appear in a directory with or without the patch - # level, even when the patch level is non-zero. - set(_boost_TEST_VERSIONS - "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}.${Boost_FIND_VERSION_PATCH}" - "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") -else() - # The user has not requested an exact version. Among known - # versions, find those that are acceptable to the user request. - set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} - "1.56.0" "1.56" "1.55.0" "1.55" "1.54.0" "1.54" - "1.53.0" "1.53" "1.52.0" "1.52" "1.51.0" "1.51" - "1.50.0" "1.50" "1.49.0" "1.49" "1.48.0" "1.48" "1.47.0" "1.47" "1.46.1" - "1.46.0" "1.46" "1.45.0" "1.45" "1.44.0" "1.44" "1.43.0" "1.43" "1.42.0" "1.42" - "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37" - "1.36.1" "1.36.0" "1.36" "1.35.1" "1.35.0" "1.35" "1.34.1" "1.34.0" - "1.34" "1.33.1" "1.33.0" "1.33") - set(_boost_TEST_VERSIONS) - if(Boost_FIND_VERSION) - set(_Boost_FIND_VERSION_SHORT "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") - # Select acceptable versions. - foreach(version ${_Boost_KNOWN_VERSIONS}) - if(NOT "${version}" VERSION_LESS "${Boost_FIND_VERSION}") - # This version is high enough. - list(APPEND _boost_TEST_VERSIONS "${version}") - elseif("${version}.99" VERSION_EQUAL "${_Boost_FIND_VERSION_SHORT}.99") - # This version is a short-form for the requested version with - # the patch level dropped. - list(APPEND _boost_TEST_VERSIONS "${version}") - endif() - endforeach() - else() - # Any version is acceptable. - set(_boost_TEST_VERSIONS "${_Boost_KNOWN_VERSIONS}") - endif() -endif() - -# The reason that we failed to find Boost. This will be set to a -# user-friendly message when we fail to find some necessary piece of -# Boost. -set(Boost_ERROR_REASON) - -if(Boost_DEBUG) - # Output some of their choices - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Boost_USE_MULTITHREADED = ${Boost_USE_MULTITHREADED}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Boost_USE_STATIC_LIBS = ${Boost_USE_STATIC_LIBS}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Boost_USE_STATIC_RUNTIME = ${Boost_USE_STATIC_RUNTIME}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Boost_ADDITIONAL_VERSIONS = ${Boost_ADDITIONAL_VERSIONS}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Boost_NO_SYSTEM_PATHS = ${Boost_NO_SYSTEM_PATHS}") -endif() - -if(WIN32) - # In windows, automatic linking is performed, so you do not have - # to specify the libraries. If you are linking to a dynamic - # runtime, then you can choose to link to either a static or a - # dynamic Boost library, the default is to do a static link. You - # can alter this for a specific library "whatever" by defining - # BOOST_WHATEVER_DYN_LINK to force Boost library "whatever" to be - # linked dynamically. Alternatively you can force all Boost - # libraries to dynamic link by defining BOOST_ALL_DYN_LINK. - - # This feature can be disabled for Boost library "whatever" by - # defining BOOST_WHATEVER_NO_LIB, or for all of Boost by defining - # BOOST_ALL_NO_LIB. - - # If you want to observe which libraries are being linked against - # then defining BOOST_LIB_DIAGNOSTIC will cause the auto-linking - # code to emit a #pragma message each time a library is selected - # for linking. - set(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC") -endif() - -_Boost_CHECK_SPELLING(Boost_ROOT) -_Boost_CHECK_SPELLING(Boost_LIBRARYDIR) -_Boost_CHECK_SPELLING(Boost_INCLUDEDIR) - -# Collect environment variable inputs as hints. Do not consider changes. -foreach(v BOOSTROOT BOOST_ROOT BOOST_INCLUDEDIR BOOST_LIBRARYDIR) - set(_env $ENV{${v}}) - if(_env) - file(TO_CMAKE_PATH "${_env}" _ENV_${v}) - else() - set(_ENV_${v} "") - endif() -endforeach() -if(NOT _ENV_BOOST_ROOT AND _ENV_BOOSTROOT) - set(_ENV_BOOST_ROOT "${_ENV_BOOSTROOT}") -endif() - -# Collect inputs and cached results. Detect changes since the last run. -if(NOT BOOST_ROOT AND BOOSTROOT) - set(BOOST_ROOT "${BOOSTROOT}") -endif() -set(_Boost_VARS_DIR - BOOST_ROOT - Boost_NO_SYSTEM_PATHS - ) - -if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Declared as CMake or Environmental Variables:") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " BOOST_ROOT = ${BOOST_ROOT}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " BOOST_INCLUDEDIR = ${BOOST_INCLUDEDIR}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " BOOST_LIBRARYDIR = ${BOOST_LIBRARYDIR}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}") -endif() - -# ------------------------------------------------------------------------ -# Search for Boost include DIR -# ------------------------------------------------------------------------ - -set(_Boost_VARS_INC BOOST_INCLUDEDIR Boost_INCLUDE_DIR Boost_ADDITIONAL_VERSIONS) -_Boost_CHANGE_DETECT(_Boost_CHANGE_INCDIR ${_Boost_VARS_DIR} ${_Boost_VARS_INC}) -# Clear Boost_INCLUDE_DIR if it did not change but other input affecting the -# location did. We will find a new one based on the new inputs. -if(_Boost_CHANGE_INCDIR AND NOT _Boost_INCLUDE_DIR_CHANGED) - unset(Boost_INCLUDE_DIR CACHE) -endif() - -if(NOT Boost_INCLUDE_DIR) - set(_boost_INCLUDE_SEARCH_DIRS "") - if(BOOST_INCLUDEDIR) - list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_INCLUDEDIR}) - elseif(_ENV_BOOST_INCLUDEDIR) - list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_INCLUDEDIR}) - endif() - - if( BOOST_ROOT ) - list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_ROOT}/include ${BOOST_ROOT}) - elseif( _ENV_BOOST_ROOT ) - list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_ROOT}/include ${_ENV_BOOST_ROOT}) - endif() - - if( Boost_NO_SYSTEM_PATHS) - list(APPEND _boost_INCLUDE_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH) - else() - list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS - C:/boost/include - C:/boost - /sw/local/include - ) - endif() - - # Try to find Boost by stepping backwards through the Boost versions - # we know about. - # Build a list of path suffixes for each version. - set(_boost_PATH_SUFFIXES) - foreach(_boost_VER ${_boost_TEST_VERSIONS}) - # Add in a path suffix, based on the required version, ideally - # we could read this from version.hpp, but for that to work we'd - # need to know the include dir already - set(_boost_BOOSTIFIED_VERSION) - - # Transform 1.35 => 1_35 and 1.36.0 => 1_36_0 - if(_boost_VER MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+") - string(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1_\\2_\\3" - _boost_BOOSTIFIED_VERSION ${_boost_VER}) - elseif(_boost_VER MATCHES "[0-9]+\\.[0-9]+") - string(REGEX REPLACE "([0-9]+)\\.([0-9]+)" "\\1_\\2" - _boost_BOOSTIFIED_VERSION ${_boost_VER}) - endif() - - list(APPEND _boost_PATH_SUFFIXES - "boost-${_boost_BOOSTIFIED_VERSION}" - "boost_${_boost_BOOSTIFIED_VERSION}" - "boost/boost-${_boost_BOOSTIFIED_VERSION}" - "boost/boost_${_boost_BOOSTIFIED_VERSION}" - ) - - endforeach() - - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Include debugging info:") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " _boost_INCLUDE_SEARCH_DIRS = ${_boost_INCLUDE_SEARCH_DIRS}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " _boost_PATH_SUFFIXES = ${_boost_PATH_SUFFIXES}") - endif() - - # Look for a standard boost header file. - find_path(Boost_INCLUDE_DIR - NAMES boost/config.hpp - HINTS ${_boost_INCLUDE_SEARCH_DIRS} - PATH_SUFFIXES ${_boost_PATH_SUFFIXES} - ) -endif() - -# ------------------------------------------------------------------------ -# Extract version information from version.hpp -# ------------------------------------------------------------------------ - -# Set Boost_FOUND based only on header location and version. -# It will be updated below for component libraries. -if(Boost_INCLUDE_DIR) - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "location of version.hpp: ${Boost_INCLUDE_DIR}/boost/version.hpp") - endif() - - # Extract Boost_VERSION and Boost_LIB_VERSION from version.hpp - set(Boost_VERSION 0) - set(Boost_LIB_VERSION "") - file(STRINGS "${Boost_INCLUDE_DIR}/boost/version.hpp" _boost_VERSION_HPP_CONTENTS REGEX "#define BOOST_(LIB_)?VERSION ") - set(_Boost_VERSION_REGEX "([0-9]+)") - set(_Boost_LIB_VERSION_REGEX "\"([0-9_]+)\"") - foreach(v VERSION LIB_VERSION) - if("${_boost_VERSION_HPP_CONTENTS}" MATCHES ".*#define BOOST_${v} ${_Boost_${v}_REGEX}.*") - set(Boost_${v} "${CMAKE_MATCH_1}") - endif() - endforeach() - unset(_boost_VERSION_HPP_CONTENTS) - - math(EXPR Boost_MAJOR_VERSION "${Boost_VERSION} / 100000") - math(EXPR Boost_MINOR_VERSION "${Boost_VERSION} / 100 % 1000") - math(EXPR Boost_SUBMINOR_VERSION "${Boost_VERSION} % 100") - - set(Boost_ERROR_REASON - "${Boost_ERROR_REASON}Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}\nBoost include path: ${Boost_INCLUDE_DIR}") - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "version.hpp reveals boost " - "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") - endif() - - if(Boost_FIND_VERSION) - # Set Boost_FOUND based on requested version. - set(_Boost_VERSION "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") - if("${_Boost_VERSION}" VERSION_LESS "${Boost_FIND_VERSION}") - set(Boost_FOUND 0) - set(_Boost_VERSION_AGE "old") - elseif(Boost_FIND_VERSION_EXACT AND - NOT "${_Boost_VERSION}" VERSION_EQUAL "${Boost_FIND_VERSION}") - set(Boost_FOUND 0) - set(_Boost_VERSION_AGE "new") - else() - set(Boost_FOUND 1) - endif() - if(NOT Boost_FOUND) - # State that we found a version of Boost that is too new or too old. - set(Boost_ERROR_REASON - "${Boost_ERROR_REASON}\nDetected version of Boost is too ${_Boost_VERSION_AGE}. Requested version was ${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") - if (Boost_FIND_VERSION_PATCH) - set(Boost_ERROR_REASON - "${Boost_ERROR_REASON}.${Boost_FIND_VERSION_PATCH}") - endif () - if (NOT Boost_FIND_VERSION_EXACT) - set(Boost_ERROR_REASON "${Boost_ERROR_REASON} (or newer)") - endif () - set(Boost_ERROR_REASON "${Boost_ERROR_REASON}.") - endif () - else() - # Caller will accept any Boost version. - set(Boost_FOUND 1) - endif() -else() - set(Boost_FOUND 0) - set(Boost_ERROR_REASON - "${Boost_ERROR_REASON}Unable to find the Boost header files. Please set BOOST_ROOT to the root directory containing Boost or BOOST_INCLUDEDIR to the directory containing Boost's headers.") -endif() - -# ------------------------------------------------------------------------ -# Suffix initialization and compiler suffix detection. -# ------------------------------------------------------------------------ - -set(_Boost_VARS_NAME - Boost_COMPILER - Boost_THREADAPI - Boost_USE_DEBUG_PYTHON - Boost_USE_MULTITHREADED - Boost_USE_STATIC_LIBS - Boost_USE_STATIC_RUNTIME - Boost_USE_STLPORT - Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS - ) -_Boost_CHANGE_DETECT(_Boost_CHANGE_LIBNAME ${_Boost_VARS_NAME}) - -# Setting some more suffixes for the library -set(Boost_LIB_PREFIX "") -if ( WIN32 AND Boost_USE_STATIC_LIBS AND NOT CYGWIN) - set(Boost_LIB_PREFIX "lib") -endif() - -if (Boost_COMPILER) - set(_boost_COMPILER ${Boost_COMPILER}) - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "using user-specified Boost_COMPILER = ${_boost_COMPILER}") - endif() -else() - # Attempt to guess the compiler suffix - # NOTE: this is not perfect yet, if you experience any issues - # please report them and use the Boost_COMPILER variable - # to work around the problems. - _Boost_GUESS_COMPILER_PREFIX(_boost_COMPILER) - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "guessed _boost_COMPILER = ${_boost_COMPILER}") - endif() -endif() - -set (_boost_MULTITHREADED "-mt") -if( NOT Boost_USE_MULTITHREADED ) - set (_boost_MULTITHREADED "") -endif() -if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "_boost_MULTITHREADED = ${_boost_MULTITHREADED}") -endif() - -#====================== -# Systematically build up the Boost ABI tag -# http://boost.org/doc/libs/1_41_0/more/getting_started/windows.html#library-naming -set( _boost_RELEASE_ABI_TAG "-") -set( _boost_DEBUG_ABI_TAG "-") -# Key Use this library when: -# s linking statically to the C++ standard library and -# compiler runtime support libraries. -if(Boost_USE_STATIC_RUNTIME) - set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}s") - set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}s") -endif() -# g using debug versions of the standard and runtime -# support libraries -if(WIN32) - if(MSVC OR "${CMAKE_CXX_COMPILER}" MATCHES "icl" - OR "${CMAKE_CXX_COMPILER}" MATCHES "icpc") - set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}g") - endif() -endif() -# y using special debug build of python -if(Boost_USE_DEBUG_PYTHON) - set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}y") -endif() -# d using a debug version of your code -set(_boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}d") -# p using the STLport standard library rather than the -# default one supplied with your compiler -if(Boost_USE_STLPORT) - set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}p") - set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}p") -endif() -# n using the STLport deprecated "native iostreams" feature -if(Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS) - set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}n") - set( _boost_DEBUG_ABI_TAG "${_boost_DEBUG_ABI_TAG}n") -endif() - -if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "_boost_RELEASE_ABI_TAG = ${_boost_RELEASE_ABI_TAG}") - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "_boost_DEBUG_ABI_TAG = ${_boost_DEBUG_ABI_TAG}") -endif() - -# ------------------------------------------------------------------------ -# Begin finding boost libraries -# ------------------------------------------------------------------------ -set(_Boost_VARS_LIB BOOST_LIBRARYDIR Boost_LIBRARY_DIR) -_Boost_CHANGE_DETECT(_Boost_CHANGE_LIBDIR ${_Boost_VARS_DIR} ${_Boost_VARS_LIB} Boost_INCLUDE_DIR) -# Clear Boost_LIBRARY_DIR if it did not change but other input affecting the -# location did. We will find a new one based on the new inputs. -if(_Boost_CHANGE_LIBDIR AND NOT _Boost_LIBRARY_DIR_CHANGED) - unset(Boost_LIBRARY_DIR CACHE) -endif() - -if(Boost_LIBRARY_DIR) - set(_boost_LIBRARY_SEARCH_DIRS ${Boost_LIBRARY_DIR} NO_DEFAULT_PATH) -else() - set(_boost_LIBRARY_SEARCH_DIRS "") - if(BOOST_LIBRARYDIR) - list(APPEND _boost_LIBRARY_SEARCH_DIRS ${BOOST_LIBRARYDIR}) - elseif(_ENV_BOOST_LIBRARYDIR) - list(APPEND _boost_LIBRARY_SEARCH_DIRS ${_ENV_BOOST_LIBRARYDIR}) - endif() - - if(BOOST_ROOT) - list(APPEND _boost_LIBRARY_SEARCH_DIRS ${BOOST_ROOT}/lib ${BOOST_ROOT}/stage/lib) - elseif(_ENV_BOOST_ROOT) - list(APPEND _boost_LIBRARY_SEARCH_DIRS ${_ENV_BOOST_ROOT}/lib ${_ENV_BOOST_ROOT}/stage/lib) - endif() - - list(APPEND _boost_LIBRARY_SEARCH_DIRS - ${Boost_INCLUDE_DIR}/lib - ${Boost_INCLUDE_DIR}/../lib - ${Boost_INCLUDE_DIR}/../lib/${CMAKE_LIBRARY_ARCHITECTURE} - ${Boost_INCLUDE_DIR}/stage/lib - ) - if( Boost_NO_SYSTEM_PATHS ) - list(APPEND _boost_LIBRARY_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH) - else() - list(APPEND _boost_LIBRARY_SEARCH_DIRS PATHS - C:/boost/lib - C:/boost - /sw/local/lib - ) - endif() -endif() - -if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "_boost_LIBRARY_SEARCH_DIRS = ${_boost_LIBRARY_SEARCH_DIRS}") -endif() - -# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES -if( Boost_USE_STATIC_LIBS ) - set( _boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) - set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES .a ) - endif() -endif() - -# We want to use the tag inline below without risking double dashes -if(_boost_RELEASE_ABI_TAG) - if(${_boost_RELEASE_ABI_TAG} STREQUAL "-") - set(_boost_RELEASE_ABI_TAG "") - endif() -endif() -if(_boost_DEBUG_ABI_TAG) - if(${_boost_DEBUG_ABI_TAG} STREQUAL "-") - set(_boost_DEBUG_ABI_TAG "") - endif() -endif() - -# The previous behavior of FindBoost when Boost_USE_STATIC_LIBS was enabled -# on WIN32 was to: -# 1. Search for static libs compiled against a SHARED C++ standard runtime library (use if found) -# 2. Search for static libs compiled against a STATIC C++ standard runtime library (use if found) -# We maintain this behavior since changing it could break people's builds. -# To disable the ambiguous behavior, the user need only -# set Boost_USE_STATIC_RUNTIME either ON or OFF. -set(_boost_STATIC_RUNTIME_WORKAROUND false) -if(WIN32 AND Boost_USE_STATIC_LIBS) - if(NOT DEFINED Boost_USE_STATIC_RUNTIME) - set(_boost_STATIC_RUNTIME_WORKAROUND true) - endif() -endif() - -# On versions < 1.35, remove the System library from the considered list -# since it wasn't added until 1.35. -if(Boost_VERSION AND Boost_FIND_COMPONENTS) - if(Boost_VERSION LESS 103500) - list(REMOVE_ITEM Boost_FIND_COMPONENTS system) - endif() -endif() - -# If the user changed any of our control inputs flush previous results. -if(_Boost_CHANGE_LIBDIR OR _Boost_CHANGE_LIBNAME) - foreach(COMPONENT ${_Boost_COMPONENTS_SEARCHED}) - string(TOUPPER ${COMPONENT} UPPERCOMPONENT) - foreach(c DEBUG RELEASE) - set(_var Boost_${UPPERCOMPONENT}_LIBRARY_${c}) - unset(${_var} CACHE) - set(${_var} "${_var}-NOTFOUND") - endforeach() - endforeach() - set(_Boost_COMPONENTS_SEARCHED "") -endif() - -foreach(COMPONENT ${Boost_FIND_COMPONENTS}) - string(TOUPPER ${COMPONENT} UPPERCOMPONENT) - - set( _boost_docstring_release "Boost ${COMPONENT} library (release)") - set( _boost_docstring_debug "Boost ${COMPONENT} library (debug)") - - # - # Find RELEASE libraries - # - set(_boost_RELEASE_NAMES - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}-${Boost_LIB_VERSION} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}-${Boost_LIB_VERSION} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} - ${Boost_LIB_PREFIX}boost_${COMPONENT} ) - if(_boost_STATIC_RUNTIME_WORKAROUND) - set(_boost_RELEASE_STATIC_ABI_TAG "-s${_boost_RELEASE_ABI_TAG}") - list(APPEND _boost_RELEASE_NAMES - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}-${Boost_LIB_VERSION} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}-${Boost_LIB_VERSION} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} ) - endif() - if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") - _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_RELEASE_NAMES ${_boost_RELEASE_NAMES}) - endif() - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Searching for ${UPPERCOMPONENT}_LIBRARY_RELEASE: ${_boost_RELEASE_NAMES}") - endif() - - # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing. - string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS}") - - _Boost_FIND_LIBRARY(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE - NAMES ${_boost_RELEASE_NAMES} - HINTS ${_boost_LIBRARY_SEARCH_DIRS_tmp} - NAMES_PER_DIR - DOC "${_boost_docstring_release}" - ) - - # - # Find DEBUG libraries - # - set(_boost_DEBUG_NAMES - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}-${Boost_LIB_VERSION} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}-${Boost_LIB_VERSION} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED} - ${Boost_LIB_PREFIX}boost_${COMPONENT} ) - if(_boost_STATIC_RUNTIME_WORKAROUND) - set(_boost_DEBUG_STATIC_ABI_TAG "-s${_boost_DEBUG_ABI_TAG}") - list(APPEND _boost_DEBUG_NAMES - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}-${Boost_LIB_VERSION} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_COMPILER}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}-${Boost_LIB_VERSION} - ${Boost_LIB_PREFIX}boost_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} ) - endif() - if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") - _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_DEBUG_NAMES ${_boost_DEBUG_NAMES}) - endif() - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "Searching for ${UPPERCOMPONENT}_LIBRARY_DEBUG: ${_boost_DEBUG_NAMES}") - endif() - - # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing. - string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS}") - - _Boost_FIND_LIBRARY(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG - NAMES ${_boost_DEBUG_NAMES} - HINTS ${_boost_LIBRARY_SEARCH_DIRS_tmp} - NAMES_PER_DIR - DOC "${_boost_docstring_debug}" - ) - - if(Boost_REALPATH) - _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE "${_boost_docstring_release}") - _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG "${_boost_docstring_debug}" ) - endif() - - _Boost_ADJUST_LIB_VARS(${UPPERCOMPONENT}) - -endforeach() - -# Restore the original find library ordering -if( Boost_USE_STATIC_LIBS ) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${_boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) -endif() - -# ------------------------------------------------------------------------ -# End finding boost libraries -# ------------------------------------------------------------------------ - -set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR}) -set(Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR}) - -# The above setting of Boost_FOUND was based only on the header files. -# Update it for the requested component libraries. -if(Boost_FOUND) - # The headers were found. Check for requested component libs. - set(_boost_CHECKED_COMPONENT FALSE) - set(_Boost_MISSING_COMPONENTS "") - foreach(COMPONENT ${Boost_FIND_COMPONENTS}) - string(TOUPPER ${COMPONENT} COMPONENT) - set(_boost_CHECKED_COMPONENT TRUE) - if(NOT Boost_${COMPONENT}_FOUND) - string(TOLOWER ${COMPONENT} COMPONENT) - list(APPEND _Boost_MISSING_COMPONENTS ${COMPONENT}) - endif() - endforeach() - - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] Boost_FOUND = ${Boost_FOUND}") - endif() - - if (_Boost_MISSING_COMPONENTS) - set(Boost_FOUND 0) - # We were unable to find some libraries, so generate a sensible - # error message that lists the libraries we were unable to find. - set(Boost_ERROR_REASON - "${Boost_ERROR_REASON}\nThe following Boost libraries could not be found:\n") - foreach(COMPONENT ${_Boost_MISSING_COMPONENTS}) - set(Boost_ERROR_REASON - "${Boost_ERROR_REASON} boost_${COMPONENT}\n") - endforeach() - - list(LENGTH Boost_FIND_COMPONENTS Boost_NUM_COMPONENTS_WANTED) - list(LENGTH _Boost_MISSING_COMPONENTS Boost_NUM_MISSING_COMPONENTS) - if (${Boost_NUM_COMPONENTS_WANTED} EQUAL ${Boost_NUM_MISSING_COMPONENTS}) - set(Boost_ERROR_REASON - "${Boost_ERROR_REASON}No Boost libraries were found. You may need to set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") - else () - set(Boost_ERROR_REASON - "${Boost_ERROR_REASON}Some (but not all) of the required Boost libraries were found. You may need to install these additional Boost libraries. Alternatively, set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") - endif () - endif () - - if( NOT Boost_LIBRARY_DIRS AND NOT _boost_CHECKED_COMPONENT ) - # Compatibility Code for backwards compatibility with CMake - # 2.4's FindBoost module. - - # Look for the boost library path. - # Note that the user may not have installed any libraries - # so it is quite possible the Boost_LIBRARY_DIRS may not exist. - set(_boost_LIB_DIR ${Boost_INCLUDE_DIR}) - - if("${_boost_LIB_DIR}" MATCHES "boost-[0-9]+") - get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) - endif() - - if("${_boost_LIB_DIR}" MATCHES "/include$") - # Strip off the trailing "/include" in the path. - get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH) - endif() - - if(EXISTS "${_boost_LIB_DIR}/lib") - set(_boost_LIB_DIR ${_boost_LIB_DIR}/lib) - else() - if(EXISTS "${_boost_LIB_DIR}/stage/lib") - set(_boost_LIB_DIR ${_boost_LIB_DIR}/stage/lib) - else() - set(_boost_LIB_DIR "") - endif() - endif() - - if(_boost_LIB_DIR AND EXISTS "${_boost_LIB_DIR}") - set(Boost_LIBRARY_DIRS ${_boost_LIB_DIR}) - endif() - - endif() -else() - # Boost headers were not found so no components were found. - foreach(COMPONENT ${Boost_FIND_COMPONENTS}) - string(TOUPPER ${COMPONENT} UPPERCOMPONENT) - set(Boost_${UPPERCOMPONENT}_FOUND 0) - endforeach() -endif() - -# ------------------------------------------------------------------------ -# Notification to end user about what was found -# ------------------------------------------------------------------------ - -set(Boost_LIBRARIES "") -if(Boost_FOUND) - if(NOT Boost_FIND_QUIETLY) - message(STATUS "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") - if(Boost_FIND_COMPONENTS) - message(STATUS "Found the following Boost libraries:") - endif() - endif() - foreach( COMPONENT ${Boost_FIND_COMPONENTS} ) - string( TOUPPER ${COMPONENT} UPPERCOMPONENT ) - if( Boost_${UPPERCOMPONENT}_FOUND ) - if(NOT Boost_FIND_QUIETLY) - message (STATUS " ${COMPONENT}") - endif() - list(APPEND Boost_LIBRARIES ${Boost_${UPPERCOMPONENT}_LIBRARY}) - endif() - endforeach() - - # Add pthread library on UNIX if thread component was found - _Boost_consider_adding_pthreads(Boost_LIBRARIES ${Boost_LIBRARIES}) -else() - if(Boost_FIND_REQUIRED) - message(SEND_ERROR "Unable to find the requested Boost libraries.\n${Boost_ERROR_REASON}") - else() - if(NOT Boost_FIND_QUIETLY) - # we opt not to automatically output Boost_ERROR_REASON here as - # it could be quite lengthy and somewhat imposing in its requests - # Since Boost is not always a required dependency we'll leave this - # up to the end-user. - if(Boost_DEBUG OR Boost_DETAILED_FAILURE_MSG) - message(STATUS "Could NOT find Boost\n${Boost_ERROR_REASON}") - else() - message(STATUS "Could NOT find Boost") - endif() - endif() - endif() -endif() - -# Configure display of cache entries in GUI. -foreach(v BOOSTROOT BOOST_ROOT ${_Boost_VARS_INC} ${_Boost_VARS_LIB}) - get_property(_type CACHE ${v} PROPERTY TYPE) - if(_type) - set_property(CACHE ${v} PROPERTY ADVANCED 1) - if("x${_type}" STREQUAL "xUNINITIALIZED") - if("x${v}" STREQUAL "xBoost_ADDITIONAL_VERSIONS") - set_property(CACHE ${v} PROPERTY TYPE STRING) - else() - set_property(CACHE ${v} PROPERTY TYPE PATH) - endif() - endif() - endif() -endforeach() - -# Record last used values of input variables so we can -# detect on the next run if the user changed them. -foreach(v - ${_Boost_VARS_INC} ${_Boost_VARS_LIB} - ${_Boost_VARS_DIR} ${_Boost_VARS_NAME} - ) - if(DEFINED ${v}) - set(_${v}_LAST "${${v}}" CACHE INTERNAL "Last used ${v} value.") - else() - unset(_${v}_LAST CACHE) - endif() -endforeach() - -# Maintain a persistent list of components requested anywhere since -# the last flush. -set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}") -list(APPEND _Boost_COMPONENTS_SEARCHED ${Boost_FIND_COMPONENTS}) -list(REMOVE_DUPLICATES _Boost_COMPONENTS_SEARCHED) -list(SORT _Boost_COMPONENTS_SEARCHED) -set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}" - CACHE INTERNAL "Components requested for this build tree.") diff --git a/libraries/fc/CMakeModules/FindReadline.cmake b/libraries/fc/CMakeModules/FindReadline.cmake index f1d0d74d92..19a15f1d0a 100644 --- a/libraries/fc/CMakeModules/FindReadline.cmake +++ b/libraries/fc/CMakeModules/FindReadline.cmake @@ -10,12 +10,14 @@ # Readline_ROOT_DIR Set this variable to the root installation of # readline if the module has problems finding the # proper installation path. +# Readline_USE_STATIC_LIBS Set to ON to force the use of the static +# libraries. Default is OFF. # # Variables defined by this module: # # READLINE_FOUND System has readline, include and lib dirs found -# Readline_INCLUDE_DIR The readline include directories. -# Readline_LIBRARY The readline library. +# Readline_INCLUDE_DIR The readline include directories. +# Readline_LIBRARIES The readline libraries. find_path(Readline_ROOT_DIR NAMES include/readline/readline.h @@ -26,24 +28,34 @@ find_path(Readline_INCLUDE_DIR HINTS ${Readline_ROOT_DIR}/include ) -find_library(Readline_LIBRARY - NAMES readline +set( _Readline_LIBRARIES readline ) +if( Readline_USE_STATIC_LIBS ) + set( _Readline_LIBRARIES libreadline.a ${_Readline_LIBRARIES} ) +endif() + +find_library(Readline_LIBRARIES + NAMES ${_Readline_LIBRARIES} HINTS ${Readline_ROOT_DIR}/lib ) -if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) +if( Readline_USE_STATIC_LIBS ) + find_library( Tinfo_LIBRARY NAMES libtinfo.a ) + list( APPEND Readline_LIBRARIES ${Readline_LIBRARIES} ${Tinfo_LIBRARY} ) +endif() + +if(Readline_INCLUDE_DIR AND Readline_LIBRARIES AND Ncurses_LIBRARY) set(READLINE_FOUND TRUE) -else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) - FIND_LIBRARY(Readline_LIBRARY NAMES readline) +else(Readline_INCLUDE_DIR AND Readline_LIBRARIES AND Ncurses_LIBRARY) + FIND_LIBRARY(Readline_LIBRARIES NAMES readline) include(FindPackageHandleStandardArgs) - FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY ) - MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY) -endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) + FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARIES ) + MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARIES) +endif(Readline_INCLUDE_DIR AND Readline_LIBRARIES AND Ncurses_LIBRARY) mark_as_advanced( Readline_ROOT_DIR Readline_INCLUDE_DIR - Readline_LIBRARY + Readline_LIBRARIES ) -MESSAGE( STATUS "Found Readline: ${Readline_LIBRARY}" ) +MESSAGE( STATUS "Found Readline: ${Readline_LIBRARIES}" ) diff --git a/libraries/fc/include/fc/crypto/blowfish.hpp b/libraries/fc/include/fc/crypto/blowfish.hpp index fcda4e56b6..9b65967062 100644 --- a/libraries/fc/include/fc/crypto/blowfish.hpp +++ b/libraries/fc/include/fc/crypto/blowfish.hpp @@ -1,178 +1,178 @@ -//////////////////////////////////////////////////////////////////////////// -/// -// Blowfish.h Header File -// -// BLOWFISH ENCRYPTION ALGORITHM -// -// encryption and decryption of Byte Strings using the Blowfish encryption Algorithm. -// Blowfish is a block cipher that encrypts data in 8-byte blocks. The algorithm consists -// of two parts: a key-expansion part and a data-ancryption part. Key expansion converts a -// variable key of at least 1 and at most 56 bytes into several subkey arrays totaling -// 4168 bytes. Blowfish has 16 rounds. Each round consists of a key-dependent permutation, -// and a key and data-dependent substitution. All operations are XORs and additions on 32-bit words. -// The only additional operations are four indexed array data lookups per round. -// Blowfish uses a large number of subkeys. These keys must be precomputed before any data -// encryption or decryption. The P-array consists of 18 32-bit subkeys: P0, P1,...,P17. -// There are also four 32-bit S-boxes with 256 entries each: S0,0, S0,1,...,S0,255; -// S1,0, S1,1,...,S1,255; S2,0, S2,1,...,S2,255; S3,0, S3,1,...,S3,255; -// -// The Electronic Code Book (ECB), Cipher Block Chaining (CBC) and Cipher Feedback modes -// are used: -// -// In ECB mode if the same block is encrypted twice with the same key, the resulting -// ciphertext blocks are the same. -// -// In CBC Mode a ciphertext block is obtained by first xoring the -// plaintext block with the previous ciphertext block, and encrypting the resulting value. -// -// In CFB mode a ciphertext block is obtained by encrypting the previous ciphertext block -// and xoring the resulting value with the plaintext -// -// The previous ciphertext block is usually stored in an Initialization Vector (IV). -// An Initialization Vector of zero is commonly used for the first block, though other -// arrangements are also in use. - -/* -http://www.counterpane.com/vectors.txt -Test vectors by Eric Young. These tests all assume Blowfish with 16 -rounds. - -All data is shown as a hex string with 012345 loading as -data[0]=0x01; -data[1]=0x23; -data[2]=0x45; -ecb test data (taken from the DES validation tests) - -key bytes clear bytes cipher bytes -0000000000000000 0000000000000000 4EF997456198DD78 -FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 51866FD5B85ECB8A -3000000000000000 1000000000000001 7D856F9A613063F2 ??? -1111111111111111 1111111111111111 2466DD878B963C9D -0123456789ABCDEF 1111111111111111 61F9C3802281B096 -1111111111111111 0123456789ABCDEF 7D0CC630AFDA1EC7 -0000000000000000 0000000000000000 4EF997456198DD78 -FEDCBA9876543210 0123456789ABCDEF 0ACEAB0FC6A0A28D -7CA110454A1A6E57 01A1D6D039776742 59C68245EB05282B -0131D9619DC1376E 5CD54CA83DEF57DA B1B8CC0B250F09A0 -07A1133E4A0B2686 0248D43806F67172 1730E5778BEA1DA4 -3849674C2602319E 51454B582DDF440A A25E7856CF2651EB -04B915BA43FEB5B6 42FD443059577FA2 353882B109CE8F1A -0113B970FD34F2CE 059B5E0851CF143A 48F4D0884C379918 -0170F175468FB5E6 0756D8E0774761D2 432193B78951FC98 -43297FAD38E373FE 762514B829BF486A 13F04154D69D1AE5 -07A7137045DA2A16 3BDD119049372802 2EEDDA93FFD39C79 -04689104C2FD3B2F 26955F6835AF609A D887E0393C2DA6E3 -37D06BB516CB7546 164D5E404F275232 5F99D04F5B163969 -1F08260D1AC2465E 6B056E18759F5CCA 4A057A3B24D3977B -584023641ABA6176 004BD6EF09176062 452031C1E4FADA8E -025816164629B007 480D39006EE762F2 7555AE39F59B87BD -49793EBC79B3258F 437540C8698F3CFA 53C55F9CB49FC019 -4FB05E1515AB73A7 072D43A077075292 7A8E7BFA937E89A3 -49E95D6D4CA229BF 02FE55778117F12A CF9C5D7A4986ADB5 -018310DC409B26D6 1D9D5C5018F728C2 D1ABB290658BC778 -1C587F1C13924FEF 305532286D6F295A 55CB3774D13EF201 -0101010101010101 0123456789ABCDEF FA34EC4847B268B2 -1F1F1F1F0E0E0E0E 0123456789ABCDEF A790795108EA3CAE -E0FEE0FEF1FEF1FE 0123456789ABCDEF C39E072D9FAC631D -0000000000000000 FFFFFFFFFFFFFFFF 014933E0CDAFF6E4 -FFFFFFFFFFFFFFFF 0000000000000000 F21E9A77B71C49BC -0123456789ABCDEF 0000000000000000 245946885754369A -FEDCBA9876543210 FFFFFFFFFFFFFFFF 6B5C5A9C5D9E0A5A - -set_key test data -data[8]= FEDCBA9876543210 -c=F9AD597C49DB005E k[ 1]=F0 -c=E91D21C1D961A6D6 k[ 2]=F0E1 -c=E9C2B70A1BC65CF3 k[ 3]=F0E1D2 -c=BE1E639408640F05 k[ 4]=F0E1D2C3 -c=B39E44481BDB1E6E k[ 5]=F0E1D2C3B4 -c=9457AA83B1928C0D k[ 6]=F0E1D2C3B4A5 -c=8BB77032F960629D k[ 7]=F0E1D2C3B4A596 -c=E87A244E2CC85E82 k[ 8]=F0E1D2C3B4A59687 -c=15750E7A4F4EC577 k[ 9]=F0E1D2C3B4A5968778 -c=122BA70B3AB64AE0 k[10]=F0E1D2C3B4A596877869 -c=3A833C9AFFC537F6 k[11]=F0E1D2C3B4A5968778695A -c=9409DA87A90F6BF2 k[12]=F0E1D2C3B4A5968778695A4B -c=884F80625060B8B4 k[13]=F0E1D2C3B4A5968778695A4B3C -c=1F85031C19E11968 k[14]=F0E1D2C3B4A5968778695A4B3C2D -c=79D9373A714CA34F k[15]=F0E1D2C3B4A5968778695A4B3C2D1E ??? -c=93142887EE3BE15C k[16]=F0E1D2C3B4A5968778695A4B3C2D1E0F -c=03429E838CE2D14B k[17]=F0E1D2C3B4A5968778695A4B3C2D1E0F00 -c=A4299E27469FF67B k[18]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011 -c=AFD5AED1C1BC96A8 k[19]=F0E1D2C3B4A5968778695A4B3C2D1E0F001122 -c=10851C0E3858DA9F k[20]=F0E1D2C3B4A5968778695A4B3C2D1E0F00112233 -c=E6F51ED79B9DB21F k[21]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344 -c=64A6E14AFD36B46F k[22]=F0E1D2C3B4A5968778695A4B3C2D1E0F001122334455 -c=80C7D7D45A5479AD k[23]=F0E1D2C3B4A5968778695A4B3C2D1E0F00112233445566 -c=05044B62FA52D080 k[24]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677 - -chaining mode test data -key[16] = 0123456789ABCDEFF0E1D2C3B4A59687 -iv[8] = FEDCBA9876543210 -data[29] = "7654321 Now is the time for " (includes trailing '\0') -data[29] = 37363534333231204E6F77206973207468652074696D6520666F722000 -cbc cipher text -cipher[32]= 6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC -cfb64 cipher text cipher[29]= -E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3 -ofb64 cipher text cipher[29]= -E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA - -*/ - -#pragma once -#include - -namespace fc { - -//Block Structure -struct sblock -{ - //Constructors - sblock(unsigned int l=0, unsigned int r=0) : m_uil(l), m_uir(r) {} - //Copy Constructor - sblock(const sblock& roBlock) : m_uil(roBlock.m_uil), m_uir(roBlock.m_uir) {} - sblock& operator^=(sblock& b) { m_uil ^= b.m_uil; m_uir ^= b.m_uir; return *this; } - unsigned int m_uil, m_uir; -}; - -class blowfish -{ -public: - enum { ECB=0, CBC=1, CFB=2 }; - - //Constructor - Initialize the P and S boxes for a given Key - blowfish(); - void start(unsigned char* ucKey, uint64_t n, const sblock& roChain = sblock(0UL,0UL)); - - //Resetting the chaining block - void reset_chain() { m_oChain = m_oChain0; } - - // encrypt/decrypt Buffer in Place - void encrypt(unsigned char* buf, uint64_t n, int iMode=CFB); - void decrypt(unsigned char* buf, uint64_t n, int iMode=CFB); - - // encrypt/decrypt from Input Buffer to Output Buffer - void encrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=CFB); - void decrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=CFB); - -//Private Functions -private: - unsigned int F(unsigned int ui); - void encrypt(sblock&); - void decrypt(sblock&); - -private: - //The Initialization Vector, by default {0, 0} - sblock m_oChain0; - sblock m_oChain; - unsigned int m_auiP[18]; - unsigned int m_auiS[4][256]; - static const unsigned int scm_auiInitP[18]; - static const unsigned int scm_auiInitS[4][256]; -}; - - -} // namespace fc - - +//////////////////////////////////////////////////////////////////////////// +/// +// Blowfish.h Header File +// +// BLOWFISH ENCRYPTION ALGORITHM +// +// encryption and decryption of Byte Strings using the Blowfish encryption Algorithm. +// Blowfish is a block cipher that encrypts data in 8-byte blocks. The algorithm consists +// of two parts: a key-expansion part and a data-ancryption part. Key expansion converts a +// variable key of at least 1 and at most 56 bytes into several subkey arrays totaling +// 4168 bytes. Blowfish has 16 rounds. Each round consists of a key-dependent permutation, +// and a key and data-dependent substitution. All operations are XORs and additions on 32-bit words. +// The only additional operations are four indexed array data lookups per round. +// Blowfish uses a large number of subkeys. These keys must be precomputed before any data +// encryption or decryption. The P-array consists of 18 32-bit subkeys: P0, P1,...,P17. +// There are also four 32-bit S-boxes with 256 entries each: S0,0, S0,1,...,S0,255; +// S1,0, S1,1,...,S1,255; S2,0, S2,1,...,S2,255; S3,0, S3,1,...,S3,255; +// +// The Electronic Code Book (ECB), Cipher Block Chaining (CBC) and Cipher Feedback modes +// are used: +// +// In ECB mode if the same block is encrypted twice with the same key, the resulting +// ciphertext blocks are the same. +// +// In CBC Mode a ciphertext block is obtained by first xoring the +// plaintext block with the previous ciphertext block, and encrypting the resulting value. +// +// In CFB mode a ciphertext block is obtained by encrypting the previous ciphertext block +// and xoring the resulting value with the plaintext +// +// The previous ciphertext block is usually stored in an Initialization Vector (IV). +// An Initialization Vector of zero is commonly used for the first block, though other +// arrangements are also in use. + +/* +http://www.counterpane.com/vectors.txt +Test vectors by Eric Young. These tests all assume Blowfish with 16 +rounds. + +All data is shown as a hex string with 012345 loading as +data[0]=0x01; +data[1]=0x23; +data[2]=0x45; +ecb test data (taken from the DES validation tests) + +key bytes clear bytes cipher bytes +0000000000000000 0000000000000000 4EF997456198DD78 +FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 51866FD5B85ECB8A +3000000000000000 1000000000000001 7D856F9A613063F2 ??? +1111111111111111 1111111111111111 2466DD878B963C9D +0123456789ABCDEF 1111111111111111 61F9C3802281B096 +1111111111111111 0123456789ABCDEF 7D0CC630AFDA1EC7 +0000000000000000 0000000000000000 4EF997456198DD78 +FEDCBA9876543210 0123456789ABCDEF 0ACEAB0FC6A0A28D +7CA110454A1A6E57 01A1D6D039776742 59C68245EB05282B +0131D9619DC1376E 5CD54CA83DEF57DA B1B8CC0B250F09A0 +07A1133E4A0B2686 0248D43806F67172 1730E5778BEA1DA4 +3849674C2602319E 51454B582DDF440A A25E7856CF2651EB +04B915BA43FEB5B6 42FD443059577FA2 353882B109CE8F1A +0113B970FD34F2CE 059B5E0851CF143A 48F4D0884C379918 +0170F175468FB5E6 0756D8E0774761D2 432193B78951FC98 +43297FAD38E373FE 762514B829BF486A 13F04154D69D1AE5 +07A7137045DA2A16 3BDD119049372802 2EEDDA93FFD39C79 +04689104C2FD3B2F 26955F6835AF609A D887E0393C2DA6E3 +37D06BB516CB7546 164D5E404F275232 5F99D04F5B163969 +1F08260D1AC2465E 6B056E18759F5CCA 4A057A3B24D3977B +584023641ABA6176 004BD6EF09176062 452031C1E4FADA8E +025816164629B007 480D39006EE762F2 7555AE39F59B87BD +49793EBC79B3258F 437540C8698F3CFA 53C55F9CB49FC019 +4FB05E1515AB73A7 072D43A077075292 7A8E7BFA937E89A3 +49E95D6D4CA229BF 02FE55778117F12A CF9C5D7A4986ADB5 +018310DC409B26D6 1D9D5C5018F728C2 D1ABB290658BC778 +1C587F1C13924FEF 305532286D6F295A 55CB3774D13EF201 +0101010101010101 0123456789ABCDEF FA34EC4847B268B2 +1F1F1F1F0E0E0E0E 0123456789ABCDEF A790795108EA3CAE +E0FEE0FEF1FEF1FE 0123456789ABCDEF C39E072D9FAC631D +0000000000000000 FFFFFFFFFFFFFFFF 014933E0CDAFF6E4 +FFFFFFFFFFFFFFFF 0000000000000000 F21E9A77B71C49BC +0123456789ABCDEF 0000000000000000 245946885754369A +FEDCBA9876543210 FFFFFFFFFFFFFFFF 6B5C5A9C5D9E0A5A + +set_key test data +data[8]= FEDCBA9876543210 +c=F9AD597C49DB005E k[ 1]=F0 +c=E91D21C1D961A6D6 k[ 2]=F0E1 +c=E9C2B70A1BC65CF3 k[ 3]=F0E1D2 +c=BE1E639408640F05 k[ 4]=F0E1D2C3 +c=B39E44481BDB1E6E k[ 5]=F0E1D2C3B4 +c=9457AA83B1928C0D k[ 6]=F0E1D2C3B4A5 +c=8BB77032F960629D k[ 7]=F0E1D2C3B4A596 +c=E87A244E2CC85E82 k[ 8]=F0E1D2C3B4A59687 +c=15750E7A4F4EC577 k[ 9]=F0E1D2C3B4A5968778 +c=122BA70B3AB64AE0 k[10]=F0E1D2C3B4A596877869 +c=3A833C9AFFC537F6 k[11]=F0E1D2C3B4A5968778695A +c=9409DA87A90F6BF2 k[12]=F0E1D2C3B4A5968778695A4B +c=884F80625060B8B4 k[13]=F0E1D2C3B4A5968778695A4B3C +c=1F85031C19E11968 k[14]=F0E1D2C3B4A5968778695A4B3C2D +c=79D9373A714CA34F k[15]=F0E1D2C3B4A5968778695A4B3C2D1E ??? +c=93142887EE3BE15C k[16]=F0E1D2C3B4A5968778695A4B3C2D1E0F +c=03429E838CE2D14B k[17]=F0E1D2C3B4A5968778695A4B3C2D1E0F00 +c=A4299E27469FF67B k[18]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011 +c=AFD5AED1C1BC96A8 k[19]=F0E1D2C3B4A5968778695A4B3C2D1E0F001122 +c=10851C0E3858DA9F k[20]=F0E1D2C3B4A5968778695A4B3C2D1E0F00112233 +c=E6F51ED79B9DB21F k[21]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344 +c=64A6E14AFD36B46F k[22]=F0E1D2C3B4A5968778695A4B3C2D1E0F001122334455 +c=80C7D7D45A5479AD k[23]=F0E1D2C3B4A5968778695A4B3C2D1E0F00112233445566 +c=05044B62FA52D080 k[24]=F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677 + +chaining mode test data +key[16] = 0123456789ABCDEFF0E1D2C3B4A59687 +iv[8] = FEDCBA9876543210 +data[29] = "7654321 Now is the time for " (includes trailing '\0') +data[29] = 37363534333231204E6F77206973207468652074696D6520666F722000 +cbc cipher text +cipher[32]= 6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC +cfb64 cipher text cipher[29]= +E73214A2822139CAF26ECF6D2EB9E76E3DA3DE04D1517200519D57A6C3 +ofb64 cipher text cipher[29]= +E73214A2822139CA62B343CC5B65587310DD908D0C241B2263C2CF80DA + +*/ + +#pragma once +#include + +namespace fc { + +//Block Structure +struct sblock +{ + //Constructors + sblock(unsigned int l=0, unsigned int r=0) : m_uil(l), m_uir(r) {} + //Copy Constructor + sblock(const sblock& roBlock) : m_uil(roBlock.m_uil), m_uir(roBlock.m_uir) {} + sblock& operator^=(sblock& b) { m_uil ^= b.m_uil; m_uir ^= b.m_uir; return *this; } + unsigned int m_uil, m_uir; +}; + +class blowfish +{ +public: + enum { ECB=0, CBC=1, CFB=2 }; + + //Constructor - Initialize the P and S boxes for a given Key + blowfish(); + void start(unsigned char* ucKey, uint64_t n, const sblock& roChain = sblock(0UL,0UL)); + + //Resetting the chaining block + void reset_chain() { m_oChain = m_oChain0; } + + // encrypt/decrypt Buffer in Place + void encrypt(unsigned char* buf, uint64_t n, int iMode=CFB); + void decrypt(unsigned char* buf, uint64_t n, int iMode=CFB); + + // encrypt/decrypt from Input Buffer to Output Buffer + void encrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=CFB); + void decrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode=CFB); + +//Private Functions +private: + unsigned int F(unsigned int ui); + void encrypt(sblock&); + void decrypt(sblock&); + +private: + //The Initialization Vector, by default {0, 0} + sblock m_oChain0; + sblock m_oChain; + unsigned int m_auiP[18]; + unsigned int m_auiS[4][256]; + static const unsigned int scm_auiInitP[18]; + static const unsigned int scm_auiInitS[4][256]; +}; + + +} // namespace fc + + diff --git a/libraries/fc/include/fc/exception/exception.hpp b/libraries/fc/include/fc/exception/exception.hpp index 4d414f7c1b..ee62d30580 100644 --- a/libraries/fc/include/fc/exception/exception.hpp +++ b/libraries/fc/include/fc/exception/exception.hpp @@ -308,7 +308,9 @@ namespace fc const char* expr ); - extern bool enable_record_assert_trip; + extern bool enable_record_assert_trip; //enables logging assertions to output and allows other flags below to take effect + extern bool enable_assert_stacktrace; //turns on stacktrace on assertions - useful for replay when assertions should not happen + extern string last_assert_expression; //filled with assertion test expression when enable_record_assert_trip } // namespace fc #if __APPLE__ @@ -475,7 +477,7 @@ namespace fc FC_RETHROW_EXCEPTION( er, LOG_LEVEL, FORMAT, __VA_ARGS__ ); \ } catch( const std::exception& e ) { \ fc::exception fce( \ - FC_LOG_MESSAGE( LOG_LEVEL, "${what}: " FORMAT,__VA_ARGS__("what",e.what())), \ + BOOST_PP_EXPAND(FC_LOG_MESSAGE( LOG_LEVEL, "${what}: " FORMAT,__VA_ARGS__("what",e.what()))), \ fc::std_exception_code,\ typeid(e).name(), \ e.what() ) ; throw fce;\ diff --git a/libraries/fc/include/fc/int_array.hpp b/libraries/fc/include/fc/int_array.hpp index 9062819908..1d127fd4ed 100644 --- a/libraries/fc/include/fc/int_array.hpp +++ b/libraries/fc/include/fc/int_array.hpp @@ -15,7 +15,6 @@ template class int_array { public: - int_array(){ memset( data, 0, sizeof(data) ); } T& at( size_t pos ) { FC_ASSERT( pos < N); return data[pos]; } const T& at( size_t pos )const { FC_ASSERT( pos < N); return data[pos]; } @@ -31,7 +30,7 @@ class int_array size_t size()const { return N; } - T data[N]; + T data[N] = { }; }; template struct get_typename< fc::int_array > diff --git a/libraries/fc/include/fc/io/raw.hpp b/libraries/fc/include/fc/io/raw.hpp index 13574eb7a8..d57cf303c0 100644 --- a/libraries/fc/include/fc/io/raw.hpp +++ b/libraries/fc/include/fc/io/raw.hpp @@ -764,6 +764,8 @@ namespace fc { typedef void result_type; template void operator()( T& v )const { + /// Here GCC 10.1 generates fake warning related to uninitialized variables. + /// GCC 10.2 has fixed this problem fc::raw::unpack( stream, v ); } }; diff --git a/libraries/fc/include/fc/log/appender.hpp b/libraries/fc/include/fc/log/appender.hpp index f1f5aa8d2d..9989b71fc5 100644 --- a/libraries/fc/include/fc/log/appender.hpp +++ b/libraries/fc/include/fc/log/appender.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace fc { class appender; @@ -27,6 +28,14 @@ namespace fc { class appender : public fc::retainable { public: + enum class time_format { + milliseconds_since_hour, + milliseconds_since_epoch, + iso_8601_seconds, + iso_8601_milliseconds, + iso_8601_microseconds + }; + typedef fc::shared_ptr ptr; template @@ -37,7 +46,14 @@ namespace fc { static appender::ptr create( const fc::string& name, const fc::string& type, const variant& args ); static appender::ptr get( const fc::string& name ); static bool register_appender( const fc::string& type, const appender_factory::ptr& f ); + static std::string format_time_as_string(time_point time, time_format format); virtual void log( const log_message& m ) = 0; }; } +FC_REFLECT_ENUM( fc::appender::time_format, + (milliseconds_since_hour) + (milliseconds_since_epoch) + (iso_8601_seconds) + (iso_8601_milliseconds) + (iso_8601_microseconds) ) diff --git a/libraries/fc/include/fc/log/console_appender.hpp b/libraries/fc/include/fc/log/console_appender.hpp index 72af377b1f..8678536eff 100644 --- a/libraries/fc/include/fc/log/console_appender.hpp +++ b/libraries/fc/include/fc/log/console_appender.hpp @@ -36,14 +36,18 @@ namespace fc struct config { - config() - :format( "${timestamp} ${context} ${file}:${line} ${method} ${level}] ${message}" ), - stream(console_appender::stream::std_error),flush(true){} + config() : + format( "${timestamp} ${context} ${file}:${line} ${method} ${level}] ${message}" ), + stream(console_appender::stream::std_error), + flush(true), + time_format(appender::time_format::milliseconds_since_hour) + {} fc::string format; console_appender::stream::type stream; std::vector level_colors; bool flush; + appender::time_format time_format; }; @@ -69,4 +73,4 @@ namespace fc FC_REFLECT_ENUM( fc::console_appender::stream::type, (std_out)(std_error) ) FC_REFLECT_ENUM( fc::console_appender::color::type, (red)(green)(brown)(blue)(magenta)(cyan)(white)(console_default) ) FC_REFLECT( fc::console_appender::level_color, (level)(color) ) -FC_REFLECT( fc::console_appender::config, (format)(stream)(level_colors)(flush) ) +FC_REFLECT( fc::console_appender::config, (format)(stream)(level_colors)(flush)(time_format) ) diff --git a/libraries/fc/include/fc/log/file_appender.hpp b/libraries/fc/include/fc/log/file_appender.hpp index a05fcd3e65..5f0b6fbb49 100644 --- a/libraries/fc/include/fc/log/file_appender.hpp +++ b/libraries/fc/include/fc/log/file_appender.hpp @@ -18,6 +18,7 @@ class file_appender : public appender { bool rotate = false; microseconds rotation_interval; microseconds rotation_limit; + appender::time_format time_format = appender::time_format::iso_8601_seconds; }; file_appender( const variant& args ); ~file_appender(); @@ -31,4 +32,4 @@ class file_appender : public appender { #include FC_REFLECT( fc::file_appender::config, - (format)(filename)(flush)(rotate)(rotation_interval)(rotation_limit) ) + (format)(filename)(flush)(rotate)(rotation_interval)(rotation_limit)(time_format) ) diff --git a/libraries/fc/include/fc/log/log_message.hpp b/libraries/fc/include/fc/log/log_message.hpp index 1928a986a5..c17c0e699c 100644 --- a/libraries/fc/include/fc/log/log_message.hpp +++ b/libraries/fc/include/fc/log/log_message.hpp @@ -8,6 +8,14 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + namespace fc { namespace detail @@ -157,6 +165,15 @@ FC_REFLECT_TYPENAME( fc::log_message ); * @param FORMAT A const char* string containing zero or more references to keys as "${key}" * @param ... A set of key/value pairs denoted as ("key",val)("key2",val2)... */ -#define FC_LOG_MESSAGE( LOG_LEVEL, FORMAT, ... ) \ - fc::log_message( FC_LOG_CONTEXT(LOG_LEVEL), FORMAT, fc::mutable_variant_object()__VA_ARGS__ ) +#define FC_LOG_MESSAGE_GENERATE_PARAMETER_NAME(VALUE) BOOST_PP_LPAREN() BOOST_PP_STRINGIZE(VALUE), fc::variant(VALUE) BOOST_PP_RPAREN() +#define FC_LOG_MESSAGE_DONT_GENERATE_PARAMETER_NAME(NAME, VALUE) BOOST_PP_LPAREN() NAME, fc::variant(VALUE) BOOST_PP_RPAREN() +#define FC_LOG_MESSAGE_GENERATE_PARAMETER_NAMES_IF_NEEDED(r, data, PARAMETER_AND_MAYBE_NAME) BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE PARAMETER_AND_MAYBE_NAME,1),FC_LOG_MESSAGE_GENERATE_PARAMETER_NAME,FC_LOG_MESSAGE_DONT_GENERATE_PARAMETER_NAME)PARAMETER_AND_MAYBE_NAME + +#define FC_LOG_MESSAGE_STRING_ONLY(LOG_LEVEL, FORMAT) \ + fc::log_message(FC_LOG_CONTEXT(LOG_LEVEL), FORMAT, fc::variant_object()) +#define FC_LOG_MESSAGE_WITH_SUBSTITUTIONS(LOG_LEVEL, FORMAT, ...) \ + fc::log_message(FC_LOG_CONTEXT(LOG_LEVEL), FORMAT, fc::mutable_variant_object() BOOST_PP_SEQ_FOR_EACH(FC_LOG_MESSAGE_GENERATE_PARAMETER_NAMES_IF_NEEDED, _, BOOST_PP_VARIADIC_SEQ_TO_SEQ(__VA_ARGS__))) + +#define FC_LOG_MESSAGE(LOG_LEVEL, ...) \ + BOOST_PP_EXPAND(BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__),1),FC_LOG_MESSAGE_STRING_ONLY,FC_LOG_MESSAGE_WITH_SUBSTITUTIONS)(LOG_LEVEL,__VA_ARGS__)) diff --git a/libraries/fc/include/fc/network/tcp_socket.hpp b/libraries/fc/include/fc/network/tcp_socket.hpp index 4c2e7d60dc..3ee6095fee 100644 --- a/libraries/fc/include/fc/network/tcp_socket.hpp +++ b/libraries/fc/include/fc/network/tcp_socket.hpp @@ -54,7 +54,11 @@ namespace fc { #ifdef _WIN64 fc::fwd my; #else - fc::fwd my; + #if BOOST_VERSION >= 107000 + fc::fwd my; + #else + fc::fwd my; + #endif #endif }; typedef std::shared_ptr tcp_socket_ptr; diff --git a/libraries/fc/include/fc/stacktrace.hpp b/libraries/fc/include/fc/stacktrace.hpp index f5ab565441..841fe0d265 100644 --- a/libraries/fc/include/fc/stacktrace.hpp +++ b/libraries/fc/include/fc/stacktrace.hpp @@ -10,7 +10,7 @@ namespace fc { -void print_stacktrace(std::ostream& out, unsigned int max_frames = 63, void* caller_overwrite_hack = nullptr); +void print_stacktrace(std::ostream& out, unsigned int max_frames = 63, void* caller_overwrite_hack = nullptr, bool addr2line = true); void print_stacktrace_on_segfault(); } diff --git a/libraries/fc/include/fc/static_variant.hpp b/libraries/fc/include/fc/static_variant.hpp index f8f32ba81f..8edad9fd12 100644 --- a/libraries/fc/include/fc/static_variant.hpp +++ b/libraries/fc/include/fc/static_variant.hpp @@ -174,6 +174,22 @@ struct type_info<> { static const size_t size = 0; }; +struct type_name_printer +{ + typedef void result_type; + + explicit type_name_printer(std::string& buffer) : _buffer(buffer) {} + + template + void operator()(const T& item) + { + _buffer = fc::get_typename::name(); + } + + private: + std::string& _buffer; +}; + } // namespace impl template @@ -200,6 +216,16 @@ class static_variant { friend struct impl::copy_construct; template friend struct impl::move_construct; + + std::string get_stored_type_name() const + { + std::string type_name("Uninitialized static_variant"); + impl::type_name_printer printer(type_name); + visit(printer); + + return type_name; + } + public: template struct tag @@ -287,9 +313,8 @@ class static_variant { void* tmp(storage); return *reinterpret_cast(tmp); } else { - FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename::name()) ); - // std::string("static_variant does not contain value of type ") + typeid(X).name() - // ); + FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}. Stored value has type: ${s}.", + ("s", get_stored_type_name())("t",fc::get_typename::name()) ); } } template @@ -302,7 +327,8 @@ class static_variant { const void* tmp(storage); return *reinterpret_cast(tmp); } else { - FC_THROW_EXCEPTION( fc::assert_exception, "static_variant does not contain a value of type ${t}", ("t",fc::get_typename::name()) ); + FC_THROW_EXCEPTION(fc::assert_exception, "static_variant does not contain a value of type ${t}. Stored value has type: ${s}.", + ("s", get_stored_type_name())("t", fc::get_typename::name())); } } template diff --git a/libraries/fc/include/fc/time.hpp b/libraries/fc/include/fc/time.hpp index 11aa96d641..c80f991348 100644 --- a/libraries/fc/include/fc/time.hpp +++ b/libraries/fc/include/fc/time.hpp @@ -49,6 +49,10 @@ namespace fc { static time_point min() { return time_point(); } operator fc::string()const; + fc::string to_iso_string_in_seconds() const; + fc::string to_iso_string_in_milliseconds() const; + fc::string to_iso_string_in_microseconds() const; + static time_point from_iso_string( const fc::string& s ); const microseconds& time_since_epoch()const { return elapsed; } diff --git a/libraries/fc/include/fc/uint128.hpp b/libraries/fc/include/fc/uint128.hpp index 98432cd69a..35e1df8a19 100644 --- a/libraries/fc/include/fc/uint128.hpp +++ b/libraries/fc/include/fc/uint128.hpp @@ -20,9 +20,7 @@ namespace fc */ class uint128 { - - - public: + public: uint128():hi(0),lo(0){} uint128( uint32_t l ):hi(0),lo(l){} uint128( int32_t l ):hi( -(l<0) ),lo(l){} @@ -39,7 +37,7 @@ namespace fc bool operator == ( const uint128& o )const{ return hi == o.hi && lo == o.lo; } bool operator != ( const uint128& o )const{ return hi != o.hi || lo != o.lo; } bool operator < ( const uint128& o )const { return (hi == o.hi) ? lo < o.lo : hi < o.hi; } - bool operator < ( const int64_t& o )const { return *this < uint128(o); } + bool operator < ( const int64_t& o )const { return *this < uint128(o); } bool operator !()const { return !(hi !=0 || lo != 0); } uint128 operator -()const { return ++uint128( ~hi, ~lo ); } uint128 operator ~()const { return uint128( ~hi, ~lo ); } @@ -73,8 +71,8 @@ namespace fc friend uint128 operator << ( const uint128& l, const uint128& r ) { return uint128(l)<<=r; } friend uint128 operator >> ( const uint128& l, const uint128& r ) { return uint128(l)>>=r; } friend bool operator > ( const uint128& l, const uint128& r ) { return r < l; } - friend bool operator > ( const uint128& l, const int64_t& r ) { return uint128(r) < l; } - friend bool operator > ( const int64_t& l, const uint128& r ) { return r < uint128(l); } + friend bool operator > ( const uint128& l, const int64_t& r ) { return uint128(r) < l; } + friend bool operator > ( const int64_t& l, const uint128& r ) { return r < uint128(l); } friend bool operator >= ( const uint128& l, const uint128& r ) { return l == r || l > r; } friend bool operator >= ( const uint128& l, const int64_t& r ) { return l >= uint128(r); } @@ -87,29 +85,30 @@ namespace fc uint32_t to_integer()const { - FC_ASSERT( hi == 0 ); - uint32_t lo32 = (uint32_t) lo; - FC_ASSERT( lo == lo32 ); - return lo32; + FC_ASSERT( hi == 0 ); + uint32_t lo32 = (uint32_t) lo; + FC_ASSERT( lo == lo32 ); + return lo32; } uint64_t to_uint64()const { - FC_ASSERT( hi == 0 ); - return lo; + FC_ASSERT( hi == 0 ); + return lo; } uint32_t low_32_bits()const { return (uint32_t) lo; } uint64_t low_bits()const { return lo; } uint64_t high_bits()const { return hi; } int64_t to_int64()const { - FC_ASSERT( hi == 0 ); - FC_ASSERT( lo <= uint64_t( std::numeric_limits::max() ) ); - return int64_t(lo); + FC_ASSERT( hi == 0 ); + FC_ASSERT( lo <= uint64_t( std::numeric_limits::max() ) ); + return int64_t(lo); } - static uint128 max_value() { - const uint64_t max64 = std::numeric_limits::max(); - return uint128( max64, max64 ); + static uint128 max_value() + { + const uint64_t max64 = std::numeric_limits::max(); + return uint128( max64, max64 ); } static void full_product( const uint128& a, const uint128& b, uint128& result_hi, uint128& result_lo ); @@ -142,14 +141,14 @@ namespace fc namespace std { - template<> - struct hash + template<> + struct hash + { + size_t operator()( const fc::uint128& s )const { - size_t operator()( const fc::uint128& s )const - { - return fc::city_hash_size_t((char*)&s, sizeof(s)); - } - }; + return fc::city_hash_size_t((char*)&s, sizeof(s)); + } + }; } FC_REFLECT( fc::uint128_t, (hi)(lo) ) diff --git a/libraries/fc/src/crypto/blowfish.cpp b/libraries/fc/src/crypto/blowfish.cpp index 5d3cbb4f3d..3bb8a11e18 100644 --- a/libraries/fc/src/crypto/blowfish.cpp +++ b/libraries/fc/src/crypto/blowfish.cpp @@ -1,624 +1,624 @@ -//////////////////////////////////////////////////////////////////////////// -/// -// BlowFish.cpp -// -// Implementation of Bruce Schneier's BLOWFISH algorithm from "Applied -// Cryptography", Second Edition. - -#include -#include -#include - -namespace fc { -//Extract low order byte -inline unsigned char Byte(unsigned int ui) -{ - return (unsigned char)(ui & 0xff); -} - -//Function F -inline unsigned int blowfish::F(unsigned int ui) -{ - return ((m_auiS[0][Byte(ui>>24)] + m_auiS[1][Byte(ui>>16)]) ^ m_auiS[2][Byte(ui>>8)]) + m_auiS[3][Byte(ui)]; -} - -//Initialization with a fixed string which consists of the hexadecimal digits of PI (less the initial 3) -//P-array, 18 32-bit subkeys -const unsigned int blowfish::scm_auiInitP[18] = { - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, - 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, - 0x9216d5d9, 0x8979fb1b -}; - -//Four 32-bit S-boxes with 256 entries each -const unsigned int blowfish::scm_auiInitS[4][256] = { - //0 - {0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, - 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, - 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, - 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, - 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, - 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, - 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, - 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, - 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, - 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, - 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, - 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, - 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, - 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, - 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, - 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, - 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, - 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, - 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, - 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, - 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, - 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, - 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, - 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, - 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, - 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, - 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, - 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, - 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, - 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, - 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, - 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, - 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, - 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, - 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, - 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, - 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, - 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, - 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, - 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, - 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, - 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, - 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, - 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, - 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, - 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, - 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, - 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, - 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, - 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, - 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, - 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, - 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, - 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a}, - - //1 - {0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, - 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, - 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, - 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, - 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, - 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, - 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, - 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, - 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, - 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, - 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, - 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, - 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, - 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, - 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, - 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, - 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, - 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, - 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, - 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, - 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, - 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, - 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, - 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, - 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, - 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, - 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, - 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, - 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, - 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, - 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, - 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, - 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, - 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, - 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, - 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, - 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, - 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, - 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, - 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, - 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, - 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, - 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, - 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, - 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, - 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, - 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, - 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, - 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, - 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, - 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, - 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, - 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, - 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7}, - - //2 - {0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, - 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, - 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, - 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, - 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, - 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, - 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, - 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, - 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, - 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, - 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, - 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, - 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, - 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, - 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, - 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, - 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, - 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, - 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, - 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, - 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, - 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, - 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, - 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, - 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, - 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, - 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, - 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, - 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, - 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, - 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, - 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, - 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, - 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, - 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, - 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, - 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, - 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, - 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, - 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, - 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, - 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, - 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, - 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, - 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, - 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, - 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, - 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, - 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, - 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, - 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, - 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, - 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, - 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0}, - - //3 - {0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, - 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, - 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, - 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, - 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, - 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, - 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, - 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, - 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, - 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, - 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, - 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, - 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, - 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, - 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, - 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, - 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, - 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, - 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, - 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, - 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, - 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, - 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, - 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, - 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, - 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, - 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, - 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, - 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, - 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, - 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, - 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, - 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, - 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, - 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, - 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, - 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, - 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, - 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, - 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, - 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, - 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, - 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, - 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, - 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, - 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, - 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, - 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, - 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, - 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, - 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, - 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, - 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, - 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6} -}; - -//Constructor - Initialize the P and S boxes for a given Key -blowfish::blowfish() -{} - -void blowfish::start(unsigned char* ucKey, uint64_t keysize, const sblock& roChain) -{ - m_oChain0 = roChain; - m_oChain = roChain; - - if(keysize<1) - FC_THROW_EXCEPTION( exception, "invalid key length" ); - //Check the Key - the key length should be between 1 and 56 bytes - if(keysize>56) - keysize = 56; - unsigned char aucLocalKey[56]; - unsigned int i, j; - memcpy(aucLocalKey, ucKey, static_cast(keysize)); - //Reflexive Initialization of the Blowfish. - //Generating the Subkeys from the Key flood P and S boxes with PI - memcpy(m_auiP, scm_auiInitP, sizeof m_auiP); - memcpy(m_auiS, scm_auiInitS, sizeof m_auiS); - //Load P boxes with key bytes - const unsigned char* p = aucLocalKey; - unsigned int x=0; - //Repeatedly cycle through the key bits until the entire P array has been XORed with key bits - uint32_t iCount = 0; - for(i=0; i<18; i++) - { - x=0; - for(int n=4; n--; ) - { - //int iVal = (int)(*p); - x <<= 8; - x |= *(p++); - iCount++; - if(iCount == keysize) - { - //All bytes used, so recycle bytes - iCount = 0; - p = aucLocalKey; - } - } - m_auiP[i] ^= x; - } - //Reflect P and S boxes through the evolving Blowfish - sblock block(0UL,0UL); //all-zero block - for(i=0; i<18; ) - encrypt(block), m_auiP[i++] = block.m_uil, m_auiP[i++] = block.m_uir; - for(j=0; j<4; j++) - for(int k=0; k<256; ) - encrypt(block), m_auiS[j][k++] = block.m_uil, m_auiS[j][k++] = block.m_uir; -} - -//Sixteen Round Encipher of Block -void blowfish::encrypt(sblock& block) -{ - unsigned int uiLeft = block.m_uil; - unsigned int uiRight = block.m_uir; - uiLeft ^= m_auiP[0]; - uiRight ^= F(uiLeft)^m_auiP[1]; uiLeft ^= F(uiRight)^m_auiP[2]; - uiRight ^= F(uiLeft)^m_auiP[3]; uiLeft ^= F(uiRight)^m_auiP[4]; - uiRight ^= F(uiLeft)^m_auiP[5]; uiLeft ^= F(uiRight)^m_auiP[6]; - uiRight ^= F(uiLeft)^m_auiP[7]; uiLeft ^= F(uiRight)^m_auiP[8]; - uiRight ^= F(uiLeft)^m_auiP[9]; uiLeft ^= F(uiRight)^m_auiP[10]; - uiRight ^= F(uiLeft)^m_auiP[11]; uiLeft ^= F(uiRight)^m_auiP[12]; - uiRight ^= F(uiLeft)^m_auiP[13]; uiLeft ^= F(uiRight)^m_auiP[14]; - uiRight ^= F(uiLeft)^m_auiP[15]; uiLeft ^= F(uiRight)^m_auiP[16]; - uiRight ^= m_auiP[17]; - block.m_uil = uiRight; - block.m_uir = uiLeft; -} - -//Sixteen Round Decipher of sblock -void blowfish::decrypt(sblock& block) -{ - unsigned int uiLeft = block.m_uil; - unsigned int uiRight = block.m_uir; - uiLeft ^= m_auiP[17]; - uiRight ^= F(uiLeft)^m_auiP[16]; uiLeft ^= F(uiRight)^m_auiP[15]; - uiRight ^= F(uiLeft)^m_auiP[14]; uiLeft ^= F(uiRight)^m_auiP[13]; - uiRight ^= F(uiLeft)^m_auiP[12]; uiLeft ^= F(uiRight)^m_auiP[11]; - uiRight ^= F(uiLeft)^m_auiP[10]; uiLeft ^= F(uiRight)^m_auiP[9]; - uiRight ^= F(uiLeft)^m_auiP[8]; uiLeft ^= F(uiRight)^m_auiP[7]; - uiRight ^= F(uiLeft)^m_auiP[6]; uiLeft ^= F(uiRight)^m_auiP[5]; - uiRight ^= F(uiLeft)^m_auiP[4]; uiLeft ^= F(uiRight)^m_auiP[3]; - uiRight ^= F(uiLeft)^m_auiP[2]; uiLeft ^= F(uiRight)^m_auiP[1]; - uiRight ^= m_auiP[0]; - block.m_uil = uiRight; - block.m_uir = uiLeft; -} - -//Semi-Portable Byte Shuffling -inline void BytesToBlock(unsigned char const* p, sblock& b) -{ - unsigned int y; - //Left - b.m_uil = 0; - y = *p++; - y <<= 24; - b.m_uil |= y; - y = *p++; - y <<= 16; - b.m_uil |= y; - y = *p++; - y <<= 8; - b.m_uil |= y; - y = *p++; - b.m_uil |= y; - //Right - b.m_uir = 0; - y = *p++; - y <<= 24; - b.m_uir |= y; - y = *p++; - y <<= 16; - b.m_uir |= y; - y = *p++; - y <<= 8; - b.m_uir |= y; - y = *p++; - b.m_uir |= y; -} - -inline void BlockToBytes(sblock const& b, unsigned char* p) -{ - unsigned int y; - //Right - y = b.m_uir; - *--p = Byte(y); - y = b.m_uir >> 8; - *--p = Byte(y); - y = b.m_uir >> 16; - *--p = Byte(y); - y = b.m_uir >> 24; - *--p = Byte(y); - //Left - y = b.m_uil; - *--p = Byte(y); - y = b.m_uil >> 8; - *--p = Byte(y); - y = b.m_uil >> 16; - *--p = Byte(y); - y = b.m_uil >> 24; - *--p = Byte(y); -} - -//encrypt Buffer in Place -//Returns false if n is multiple of 8 -void blowfish::encrypt(unsigned char* buf, uint64_t n, int iMode) -{ - //Check the buffer's length - should be > 0 and multiple of 8 - if((n==0)||(n%8!=0)) - FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); - sblock work; - if(iMode == CBC) //CBC mode, using the Chain - { - sblock chain(m_oChain); - for(; n >= 8; n -= 8) - { - BytesToBlock(buf, work); - work ^= chain; - encrypt(work); - chain = work; - BlockToBytes(work, buf+=8); - } - } - else if(iMode == CFB) //CFB mode, using the Chain - { - sblock chain(m_oChain); - for(; n >= 8; n -= 8) - { - encrypt(chain); - BytesToBlock(buf, work); - work ^= chain; - chain = work; - BlockToBytes(work, buf+=8); - } - } - else //ECB mode, not using the Chain - { - for(; n >= 8; n -= 8) - { - BytesToBlock(buf, work); - encrypt(work); - BlockToBytes(work, buf+=8); - } - } -} - -//decrypt Buffer in Place -//Returns false if n is multiple of 8 -void blowfish::decrypt(unsigned char* buf, uint64_t n, int iMode) -{ - //Check the buffer's length - should be > 0 and multiple of 8 - if((n==0)||(n%8!=0)) - FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); - sblock work; - if(iMode == CBC) //CBC mode, using the Chain - { - sblock crypt, chain(m_oChain); - for(; n >= 8; n -= 8) - { - BytesToBlock(buf, work); - crypt = work; - decrypt(work); - work ^= chain; - chain = crypt; - BlockToBytes(work, buf+=8); - } - } - else if(iMode == CFB) //CFB mode, using the Chain, not using decrypt() - { - sblock crypt, chain(m_oChain); - for(; n >= 8; n -= 8) - { - BytesToBlock(buf, work); - encrypt(chain); - crypt = work; - work ^= chain; - chain = crypt; - BlockToBytes(work, buf+=8); - } - } - else //ECB mode, not using the Chain - { - for(; n >= 8; n -= 8) - { - BytesToBlock(buf, work); - decrypt(work); - BlockToBytes(work, buf+=8); - } - } -} - -//encrypt from Input Buffer to Output Buffer -//Returns false if n is multiple of 8 -void blowfish::encrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode) -{ - //Check the buffer's length - should be > 0 and multiple of 8 - if((n==0)||(n%8!=0)) - FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); - sblock work; - if(iMode == CBC) //CBC mode, using the Chain - { - sblock chain(m_oChain); - for(; n >= 8; n -= 8, in += 8) - { - BytesToBlock(in, work); - work ^= chain; - encrypt(work); - chain = work; - BlockToBytes(work, out+=8); - } - } - else if(iMode == CFB) //CFB mode, using the Chain - { - sblock chain(m_oChain); - for(; n >= 8; n -= 8, in += 8) - { - encrypt(chain); - BytesToBlock(in, work); - work ^= chain; - chain = work; - BlockToBytes(work, out+=8); - } - } - else //ECB mode, not using the Chain - { - for(; n >= 8; n -= 8, in += 8) - { - BytesToBlock(in, work); - encrypt(work); - BlockToBytes(work, out+=8); - } - } -} - -//decrypt from Input Buffer to Output Buffer -//Returns false if n is multiple of 8 -void blowfish::decrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode) -{ - //Check the buffer's length - should be > 0 and multiple of 8 - if((n==0)||(n%8!=0)) - FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); - sblock work; - if(iMode == CBC) //CBC mode, using the Chain - { - sblock crypt, chain(m_oChain); - for(; n >= 8; n -= 8, in += 8) - { - BytesToBlock(in, work); - crypt = work; - decrypt(work); - work ^= chain; - chain = crypt; - BlockToBytes(work, out+=8); - } - } - else if(iMode == CFB) //CFB mode, using the Chain, not using decrypt() - { - sblock crypt, chain(m_oChain); - for(; n >= 8; n -= 8, in += 8) - { - BytesToBlock(in, work); - encrypt(chain); - crypt = work; - work ^= chain; - chain = crypt; - BlockToBytes(work, out+=8); - } - } - else //ECB mode, not using the Chain - { - for(; n >= 8; n -= 8, in += 8) - { - BytesToBlock(in, work); - decrypt(work); - BlockToBytes(work, out+=8); - } - } -} - -} // namespace fc +//////////////////////////////////////////////////////////////////////////// +/// +// BlowFish.cpp +// +// Implementation of Bruce Schneier's BLOWFISH algorithm from "Applied +// Cryptography", Second Edition. + +#include +#include +#include + +namespace fc { +//Extract low order byte +inline unsigned char Byte(unsigned int ui) +{ + return (unsigned char)(ui & 0xff); +} + +//Function F +inline unsigned int blowfish::F(unsigned int ui) +{ + return ((m_auiS[0][Byte(ui>>24)] + m_auiS[1][Byte(ui>>16)]) ^ m_auiS[2][Byte(ui>>8)]) + m_auiS[3][Byte(ui)]; +} + +//Initialization with a fixed string which consists of the hexadecimal digits of PI (less the initial 3) +//P-array, 18 32-bit subkeys +const unsigned int blowfish::scm_auiInitP[18] = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b +}; + +//Four 32-bit S-boxes with 256 entries each +const unsigned int blowfish::scm_auiInitS[4][256] = { + //0 + {0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a}, + + //1 + {0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7}, + + //2 + {0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0}, + + //3 + {0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6} +}; + +//Constructor - Initialize the P and S boxes for a given Key +blowfish::blowfish() +{} + +void blowfish::start(unsigned char* ucKey, uint64_t keysize, const sblock& roChain) +{ + m_oChain0 = roChain; + m_oChain = roChain; + + if(keysize<1) + FC_THROW_EXCEPTION( exception, "invalid key length" ); + //Check the Key - the key length should be between 1 and 56 bytes + if(keysize>56) + keysize = 56; + unsigned char aucLocalKey[56]; + unsigned int i, j; + memcpy(aucLocalKey, ucKey, static_cast(keysize)); + //Reflexive Initialization of the Blowfish. + //Generating the Subkeys from the Key flood P and S boxes with PI + memcpy(m_auiP, scm_auiInitP, sizeof m_auiP); + memcpy(m_auiS, scm_auiInitS, sizeof m_auiS); + //Load P boxes with key bytes + const unsigned char* p = aucLocalKey; + unsigned int x=0; + //Repeatedly cycle through the key bits until the entire P array has been XORed with key bits + uint32_t iCount = 0; + for(i=0; i<18; i++) + { + x=0; + for(int n=4; n--; ) + { + //int iVal = (int)(*p); + x <<= 8; + x |= *(p++); + iCount++; + if(iCount == keysize) + { + //All bytes used, so recycle bytes + iCount = 0; + p = aucLocalKey; + } + } + m_auiP[i] ^= x; + } + //Reflect P and S boxes through the evolving Blowfish + sblock block(0UL,0UL); //all-zero block + for(i=0; i<18; ) + encrypt(block), m_auiP[i++] = block.m_uil, m_auiP[i++] = block.m_uir; + for(j=0; j<4; j++) + for(int k=0; k<256; ) + encrypt(block), m_auiS[j][k++] = block.m_uil, m_auiS[j][k++] = block.m_uir; +} + +//Sixteen Round Encipher of Block +void blowfish::encrypt(sblock& block) +{ + unsigned int uiLeft = block.m_uil; + unsigned int uiRight = block.m_uir; + uiLeft ^= m_auiP[0]; + uiRight ^= F(uiLeft)^m_auiP[1]; uiLeft ^= F(uiRight)^m_auiP[2]; + uiRight ^= F(uiLeft)^m_auiP[3]; uiLeft ^= F(uiRight)^m_auiP[4]; + uiRight ^= F(uiLeft)^m_auiP[5]; uiLeft ^= F(uiRight)^m_auiP[6]; + uiRight ^= F(uiLeft)^m_auiP[7]; uiLeft ^= F(uiRight)^m_auiP[8]; + uiRight ^= F(uiLeft)^m_auiP[9]; uiLeft ^= F(uiRight)^m_auiP[10]; + uiRight ^= F(uiLeft)^m_auiP[11]; uiLeft ^= F(uiRight)^m_auiP[12]; + uiRight ^= F(uiLeft)^m_auiP[13]; uiLeft ^= F(uiRight)^m_auiP[14]; + uiRight ^= F(uiLeft)^m_auiP[15]; uiLeft ^= F(uiRight)^m_auiP[16]; + uiRight ^= m_auiP[17]; + block.m_uil = uiRight; + block.m_uir = uiLeft; +} + +//Sixteen Round Decipher of sblock +void blowfish::decrypt(sblock& block) +{ + unsigned int uiLeft = block.m_uil; + unsigned int uiRight = block.m_uir; + uiLeft ^= m_auiP[17]; + uiRight ^= F(uiLeft)^m_auiP[16]; uiLeft ^= F(uiRight)^m_auiP[15]; + uiRight ^= F(uiLeft)^m_auiP[14]; uiLeft ^= F(uiRight)^m_auiP[13]; + uiRight ^= F(uiLeft)^m_auiP[12]; uiLeft ^= F(uiRight)^m_auiP[11]; + uiRight ^= F(uiLeft)^m_auiP[10]; uiLeft ^= F(uiRight)^m_auiP[9]; + uiRight ^= F(uiLeft)^m_auiP[8]; uiLeft ^= F(uiRight)^m_auiP[7]; + uiRight ^= F(uiLeft)^m_auiP[6]; uiLeft ^= F(uiRight)^m_auiP[5]; + uiRight ^= F(uiLeft)^m_auiP[4]; uiLeft ^= F(uiRight)^m_auiP[3]; + uiRight ^= F(uiLeft)^m_auiP[2]; uiLeft ^= F(uiRight)^m_auiP[1]; + uiRight ^= m_auiP[0]; + block.m_uil = uiRight; + block.m_uir = uiLeft; +} + +//Semi-Portable Byte Shuffling +inline void BytesToBlock(unsigned char const* p, sblock& b) +{ + unsigned int y; + //Left + b.m_uil = 0; + y = *p++; + y <<= 24; + b.m_uil |= y; + y = *p++; + y <<= 16; + b.m_uil |= y; + y = *p++; + y <<= 8; + b.m_uil |= y; + y = *p++; + b.m_uil |= y; + //Right + b.m_uir = 0; + y = *p++; + y <<= 24; + b.m_uir |= y; + y = *p++; + y <<= 16; + b.m_uir |= y; + y = *p++; + y <<= 8; + b.m_uir |= y; + y = *p++; + b.m_uir |= y; +} + +inline void BlockToBytes(sblock const& b, unsigned char* p) +{ + unsigned int y; + //Right + y = b.m_uir; + *--p = Byte(y); + y = b.m_uir >> 8; + *--p = Byte(y); + y = b.m_uir >> 16; + *--p = Byte(y); + y = b.m_uir >> 24; + *--p = Byte(y); + //Left + y = b.m_uil; + *--p = Byte(y); + y = b.m_uil >> 8; + *--p = Byte(y); + y = b.m_uil >> 16; + *--p = Byte(y); + y = b.m_uil >> 24; + *--p = Byte(y); +} + +//encrypt Buffer in Place +//Returns false if n is multiple of 8 +void blowfish::encrypt(unsigned char* buf, uint64_t n, int iMode) +{ + //Check the buffer's length - should be > 0 and multiple of 8 + if((n==0)||(n%8!=0)) + FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); + sblock work; + if(iMode == CBC) //CBC mode, using the Chain + { + sblock chain(m_oChain); + for(; n >= 8; n -= 8) + { + BytesToBlock(buf, work); + work ^= chain; + encrypt(work); + chain = work; + BlockToBytes(work, buf+=8); + } + } + else if(iMode == CFB) //CFB mode, using the Chain + { + sblock chain(m_oChain); + for(; n >= 8; n -= 8) + { + encrypt(chain); + BytesToBlock(buf, work); + work ^= chain; + chain = work; + BlockToBytes(work, buf+=8); + } + } + else //ECB mode, not using the Chain + { + for(; n >= 8; n -= 8) + { + BytesToBlock(buf, work); + encrypt(work); + BlockToBytes(work, buf+=8); + } + } +} + +//decrypt Buffer in Place +//Returns false if n is multiple of 8 +void blowfish::decrypt(unsigned char* buf, uint64_t n, int iMode) +{ + //Check the buffer's length - should be > 0 and multiple of 8 + if((n==0)||(n%8!=0)) + FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); + sblock work; + if(iMode == CBC) //CBC mode, using the Chain + { + sblock crypt, chain(m_oChain); + for(; n >= 8; n -= 8) + { + BytesToBlock(buf, work); + crypt = work; + decrypt(work); + work ^= chain; + chain = crypt; + BlockToBytes(work, buf+=8); + } + } + else if(iMode == CFB) //CFB mode, using the Chain, not using decrypt() + { + sblock crypt, chain(m_oChain); + for(; n >= 8; n -= 8) + { + BytesToBlock(buf, work); + encrypt(chain); + crypt = work; + work ^= chain; + chain = crypt; + BlockToBytes(work, buf+=8); + } + } + else //ECB mode, not using the Chain + { + for(; n >= 8; n -= 8) + { + BytesToBlock(buf, work); + decrypt(work); + BlockToBytes(work, buf+=8); + } + } +} + +//encrypt from Input Buffer to Output Buffer +//Returns false if n is multiple of 8 +void blowfish::encrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode) +{ + //Check the buffer's length - should be > 0 and multiple of 8 + if((n==0)||(n%8!=0)) + FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); + sblock work; + if(iMode == CBC) //CBC mode, using the Chain + { + sblock chain(m_oChain); + for(; n >= 8; n -= 8, in += 8) + { + BytesToBlock(in, work); + work ^= chain; + encrypt(work); + chain = work; + BlockToBytes(work, out+=8); + } + } + else if(iMode == CFB) //CFB mode, using the Chain + { + sblock chain(m_oChain); + for(; n >= 8; n -= 8, in += 8) + { + encrypt(chain); + BytesToBlock(in, work); + work ^= chain; + chain = work; + BlockToBytes(work, out+=8); + } + } + else //ECB mode, not using the Chain + { + for(; n >= 8; n -= 8, in += 8) + { + BytesToBlock(in, work); + encrypt(work); + BlockToBytes(work, out+=8); + } + } +} + +//decrypt from Input Buffer to Output Buffer +//Returns false if n is multiple of 8 +void blowfish::decrypt(const unsigned char* in, unsigned char* out, uint64_t n, int iMode) +{ + //Check the buffer's length - should be > 0 and multiple of 8 + if((n==0)||(n%8!=0)) + FC_THROW_EXCEPTION( exception, "invalid buffer length ${n}, not multiple of 8", ("n", n) ); + sblock work; + if(iMode == CBC) //CBC mode, using the Chain + { + sblock crypt, chain(m_oChain); + for(; n >= 8; n -= 8, in += 8) + { + BytesToBlock(in, work); + crypt = work; + decrypt(work); + work ^= chain; + chain = crypt; + BlockToBytes(work, out+=8); + } + } + else if(iMode == CFB) //CFB mode, using the Chain, not using decrypt() + { + sblock crypt, chain(m_oChain); + for(; n >= 8; n -= 8, in += 8) + { + BytesToBlock(in, work); + encrypt(chain); + crypt = work; + work ^= chain; + chain = crypt; + BlockToBytes(work, out+=8); + } + } + else //ECB mode, not using the Chain + { + for(; n >= 8; n -= 8, in += 8) + { + BytesToBlock(in, work); + decrypt(work); + BlockToBytes(work, out+=8); + } + } +} + +} // namespace fc diff --git a/libraries/fc/src/exception.cpp b/libraries/fc/src/exception.cpp index a9f4c641e9..f547f606a0 100644 --- a/libraries/fc/src/exception.cpp +++ b/libraries/fc/src/exception.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -250,6 +251,8 @@ namespace fc const char* expr ) { + last_assert_expression = expr; + fc::mutable_variant_object assert_trip_info = fc::mutable_variant_object() ("source_file", filename) @@ -259,9 +262,18 @@ namespace fc std::cout << "FC_ASSERT triggered: " << fc::json::to_string( assert_trip_info ) << "\n"; - return; + + if( enable_assert_stacktrace ) + { + std::stringstream out; + out << "FC_ASSERT / CHAINBASE_THROW_EXCEPTION!" << std::endl; + print_stacktrace( out, 128, nullptr, false ); + wlog( out.str() ); + } } bool enable_record_assert_trip = false; + bool enable_assert_stacktrace = false; + string last_assert_expression; } // fc diff --git a/libraries/fc/src/log/appender.cpp b/libraries/fc/src/log/appender.cpp index a3e5306f5c..3f3dc6de4c 100644 --- a/libraries/fc/src/log/appender.cpp +++ b/libraries/fc/src/log/appender.cpp @@ -45,6 +45,30 @@ namespace fc { return ap; } + /* static */ std::string appender::format_time_as_string(time_point time, time_format format) + { + std::stringstream result; + switch (format) { + case appender::time_format::milliseconds_since_epoch: + result << time.time_since_epoch().count() / 1000 << "ms"; + break; + case appender::time_format::iso_8601_seconds: + result << time.to_iso_string_in_seconds(); + break; + case appender::time_format::iso_8601_milliseconds: + result << time.to_iso_string_in_milliseconds(); + break; + case appender::time_format::iso_8601_microseconds: + result << time.to_iso_string_in_microseconds(); + break; + case appender::time_format::milliseconds_since_hour: + default: + result << (time.time_since_epoch().count() % (1000ll * 1000ll * 60ll * 60)) / 1000 << "ms"; + } + return result.str(); + } + + /* Assiging a function return to a static variable allows code exectution on initialization. In a perfect world, we would not do this. This results in an diff --git a/libraries/fc/src/log/console_appender.cpp b/libraries/fc/src/log/console_appender.cpp index 870094aebf..903a674f76 100644 --- a/libraries/fc/src/log/console_appender.cpp +++ b/libraries/fc/src/log/console_appender.cpp @@ -1,155 +1,155 @@ -#include -#include -#include -#include -#include -#include -#ifndef WIN32 -#include -#endif -#include -#define COLOR_CONSOLE 1 -#include "console_defines.h" -#include -#include -#include -#include - - -namespace fc { - - class console_appender::impl { - public: - config cfg; - color::type lc[log_level::off+1]; -#ifdef WIN32 - HANDLE console_handle; -#endif - }; - - console_appender::console_appender( const variant& args ) - :my(new impl) - { - configure( args.as() ); - } - - console_appender::console_appender( const config& cfg ) - :my(new impl) - { - configure( cfg ); - } - console_appender::console_appender() - :my(new impl){} - - - void console_appender::configure( const config& console_appender_config ) - { try { -#ifdef WIN32 - my->console_handle = INVALID_HANDLE_VALUE; -#endif - my->cfg = console_appender_config; -#ifdef WIN32 - if (my->cfg.stream = stream::std_error) - my->console_handle = GetStdHandle(STD_ERROR_HANDLE); - else if (my->cfg.stream = stream::std_out) - my->console_handle = GetStdHandle(STD_OUTPUT_HANDLE); -#endif - - for( int i = 0; i < log_level::off+1; ++i ) - my->lc[i] = color::console_default; - for( auto itr = my->cfg.level_colors.begin(); itr != my->cfg.level_colors.end(); ++itr ) - my->lc[itr->level] = itr->color; - } FC_CAPTURE_AND_RETHROW( (console_appender_config) ) } - - console_appender::~console_appender() {} - - #ifdef WIN32 - static WORD - #else - static const char* - #endif - get_console_color(console_appender::color::type t ) { - switch( t ) { - case console_appender::color::red: return CONSOLE_RED; - case console_appender::color::green: return CONSOLE_GREEN; - case console_appender::color::brown: return CONSOLE_BROWN; - case console_appender::color::blue: return CONSOLE_BLUE; - case console_appender::color::magenta: return CONSOLE_MAGENTA; - case console_appender::color::cyan: return CONSOLE_CYAN; - case console_appender::color::white: return CONSOLE_WHITE; - case console_appender::color::console_default: - default: - return CONSOLE_DEFAULT; - } - } - - boost::mutex& log_mutex() { - static boost::mutex m; return m; - } - - void console_appender::log( const log_message& m ) { - //fc::string message = fc::format_string( m.get_format(), m.get_data() ); - //fc::variant lmsg(m); - - FILE* out = stream::std_error ? stderr : stdout; - - //fc::string fmt_str = fc::format_string( cfg.format, mutable_variant_object(m.get_context())( "message", message) ); - std::stringstream file_line; - file_line << m.get_context().get_file() <<":"< lock(log_mutex()); - - print( line.str(), my->lc[m.get_context().get_log_level()] ); - - fprintf( out, "\n" ); - - if( my->cfg.flush ) fflush( out ); - } - - void console_appender::print( const std::string& text, color::type text_color ) - { - FILE* out = stream::std_error ? stderr : stdout; - - #ifdef WIN32 - if (my->console_handle != INVALID_HANDLE_VALUE) - SetConsoleTextAttribute(my->console_handle, get_console_color(text_color)); - #else - if(isatty(fileno(out))) fprintf( out, "\r%s", get_console_color( text_color ) ); - #endif - - if( text.size() ) - fprintf( out, "%s", text.c_str() ); //fmt_str.c_str() ); - - #ifdef WIN32 - if (my->console_handle != INVALID_HANDLE_VALUE) - SetConsoleTextAttribute(my->console_handle, CONSOLE_DEFAULT); - #else - if(isatty(fileno(out))) fprintf( out, "\r%s", CONSOLE_DEFAULT ); - #endif - - if( my->cfg.flush ) fflush( out ); - } - -} +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#define COLOR_CONSOLE 1 +#include "console_defines.h" +#include +#include +#include +#include + + +namespace fc { + + class console_appender::impl { + public: + config cfg; + color::type lc[log_level::off+1]; +#ifdef WIN32 + HANDLE console_handle; +#endif + }; + + console_appender::console_appender( const variant& args ) + :my(new impl) + { + configure( args.as() ); + } + + console_appender::console_appender( const config& cfg ) + :my(new impl) + { + configure( cfg ); + } + console_appender::console_appender() + :my(new impl){} + + + void console_appender::configure( const config& console_appender_config ) + { try { +#ifdef WIN32 + my->console_handle = INVALID_HANDLE_VALUE; +#endif + my->cfg = console_appender_config; +#ifdef WIN32 + if (my->cfg.stream = stream::std_error) + my->console_handle = GetStdHandle(STD_ERROR_HANDLE); + else if (my->cfg.stream = stream::std_out) + my->console_handle = GetStdHandle(STD_OUTPUT_HANDLE); +#endif + + for( int i = 0; i < log_level::off+1; ++i ) + my->lc[i] = color::console_default; + for( auto itr = my->cfg.level_colors.begin(); itr != my->cfg.level_colors.end(); ++itr ) + my->lc[itr->level] = itr->color; + } FC_CAPTURE_AND_RETHROW( (console_appender_config) ) } + + console_appender::~console_appender() {} + + #ifdef WIN32 + static WORD + #else + static const char* + #endif + get_console_color(console_appender::color::type t ) { + switch( t ) { + case console_appender::color::red: return CONSOLE_RED; + case console_appender::color::green: return CONSOLE_GREEN; + case console_appender::color::brown: return CONSOLE_BROWN; + case console_appender::color::blue: return CONSOLE_BLUE; + case console_appender::color::magenta: return CONSOLE_MAGENTA; + case console_appender::color::cyan: return CONSOLE_CYAN; + case console_appender::color::white: return CONSOLE_WHITE; + case console_appender::color::console_default: + default: + return CONSOLE_DEFAULT; + } + } + + boost::mutex& log_mutex() { + static boost::mutex m; return m; + } + + void console_appender::log( const log_message& m ) { + //fc::string message = fc::format_string( m.get_format(), m.get_data() ); + //fc::variant lmsg(m); + + FILE* out = stream::std_error ? stderr : stdout; + + //fc::string fmt_str = fc::format_string( cfg.format, mutable_variant_object(m.get_context())( "message", message) ); + std::stringstream file_line; + file_line << m.get_context().get_file() <<":"<cfg.time_format); + line << " " << std::setw(30) << std::left << file_line.str(); + + auto me = m.get_context().get_method(); + // strip all leading scopes... + if( me.size() ) + { + uint32_t p = 0; + for( uint32_t i = 0;i < me.size(); ++i ) + { + if( me[i] == ':' ) p = i; + } + + if( me[p] == ':' ) ++p; + line << std::setw( 20 ) << std::left << m.get_context().get_method().substr(p,20).c_str() <<" "; + } + line << "] "; + fc::string message = fc::format_string( m.get_format(), m.get_data() ); + line << message;//.c_str(); + + fc::unique_lock lock(log_mutex()); + + print( line.str(), my->lc[m.get_context().get_log_level()] ); + + fprintf( out, "\n" ); + + if( my->cfg.flush ) fflush( out ); + } + + void console_appender::print( const std::string& text, color::type text_color ) + { + FILE* out = stream::std_error ? stderr : stdout; + + #ifdef WIN32 + if (my->console_handle != INVALID_HANDLE_VALUE) + SetConsoleTextAttribute(my->console_handle, get_console_color(text_color)); + #else + if(isatty(fileno(out))) fprintf( out, "\r%s", get_console_color( text_color ) ); + #endif + + if( text.size() ) + fprintf( out, "%s", text.c_str() ); //fmt_str.c_str() ); + + #ifdef WIN32 + if (my->console_handle != INVALID_HANDLE_VALUE) + SetConsoleTextAttribute(my->console_handle, CONSOLE_DEFAULT); + #else + if(isatty(fileno(out))) fprintf( out, "\r%s", CONSOLE_DEFAULT ); + #endif + + if( my->cfg.flush ) fflush( out ); + } + +} diff --git a/libraries/fc/src/log/file_appender.cpp b/libraries/fc/src/log/file_appender.cpp index 28e0b8b35b..2fba950843 100644 --- a/libraries/fc/src/log/file_appender.cpp +++ b/libraries/fc/src/log/file_appender.cpp @@ -161,9 +161,8 @@ namespace fc { void file_appender::log( const log_message& m ) { std::stringstream line; - //line << (m.get_context().get_timestamp().time_since_epoch().count() % (1000ll*1000ll*60ll*60))/1000 <<"ms "; - line << string(m.get_context().get_timestamp()) << " "; - line << std::setw( 21 ) << (m.get_context().get_task_name()).c_str() << " "; + line << appender::format_time_as_string(m.get_context().get_timestamp(), my->cfg.time_format); + line << " " << std::setw( 21 ) << (m.get_context().get_task_name()).c_str() << " "; string method_name = m.get_context().get_method(); // strip all leading scopes... diff --git a/libraries/fc/src/stacktrace.cpp b/libraries/fc/src/stacktrace.cpp index 18d663d9d0..a21a865037 100644 --- a/libraries/fc/src/stacktrace.cpp +++ b/libraries/fc/src/stacktrace.cpp @@ -81,92 +81,92 @@ void print_stacktrace_linenums( void** addrlist, int addrlen ) std::cerr << output << std::endl; } -void print_stacktrace(std::ostream& out, unsigned int max_frames /* = 63 */, void* caller_overwrite_hack /* = nullptr */ ) +void print_stacktrace(std::ostream& out, unsigned int max_frames /* = 63 */, void* caller_overwrite_hack /* = nullptr */, bool addr2line /* = true */ ) { - out << "stack trace:" << std::endl; + out << "stack trace:" << std::endl; - // storage array for stack trace address data - void* addrlist[max_frames+1]; + // storage array for stack trace address data + void* addrlist[max_frames+1]; - // retrieve current stack addresses - int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*)); + // retrieve current stack addresses + int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*)); - if (addrlen == 0) { - out << " " << std::endl; - return; - } + if( addrlen == 0 ) + { + out << " " << std::endl; + return; + } - if( caller_overwrite_hack != nullptr ) - addrlist[2] = caller_overwrite_hack; + if( caller_overwrite_hack != nullptr ) + addrlist[2] = caller_overwrite_hack; - // resolve addresses into strings containing "filename(function+address)", - // this array must be free()-ed - char** symbollist = backtrace_symbols(addrlist, addrlen); + // resolve addresses into strings containing "filename(function+address)", + // this array must be free()-ed + char** symbollist = backtrace_symbols(addrlist, addrlen); - // allocate string which will be filled with the demangled function name - size_t funcnamesize = 256; - char* funcname = (char*)malloc(funcnamesize); + // allocate string which will be filled with the demangled function name + size_t funcnamesize = 256; + char* funcname = (char*)malloc(funcnamesize); - // iterate over the returned symbol lines. skip the first, it is the - // address of this function. - for (int i = 1; i < addrlen; i++) - { - char *begin_name = 0, *begin_offset = 0, *end_offset = 0; - - // find parentheses and +address offset surrounding the mangled name: - // ./module(function+0x15c) [0x8048a6d] - for (char *p = symbollist[i]; *p; ++p) - { - if (*p == '(') - begin_name = p; - else if (*p == '+') - begin_offset = p; - else if (*p == ')' && begin_offset) - { - end_offset = p; - break; - } - } - - out << " 0x" << std::setfill('0') << std::setw(16) << std::hex << std::noshowbase << uint64_t(addrlist[i]); - - if (begin_name && begin_offset && end_offset - && begin_name < begin_offset) - { - *begin_name++ = '\0'; - *begin_offset++ = '\0'; - *end_offset = '\0'; - - // mangled name is now in [begin_name, begin_offset) and caller - // offset in [begin_offset, end_offset). now apply - // __cxa_demangle(): - - int status; - char* ret = abi::__cxa_demangle(begin_name, - funcname, &funcnamesize, &status); - if (status == 0) - { - funcname = ret; // use possibly realloc()-ed string - out << " " << symbollist[i] << " : " << funcname << "+" << begin_offset << std::endl; - } - else - { - // demangling failed. Output function name as a C function with - // no arguments. - out << " " << symbollist[i] << " : " << begin_name << "+" << begin_offset << std::endl; - } - } - else - { - // couldn't parse the line? print the whole line. - out << " " << symbollist[i] << std::endl; - } - } + // iterate over the returned symbol lines. skip the first, it is the + // address of this function. + for (int i = 1; i < addrlen; i++) + { + char *begin_name = 0, *begin_offset = 0, *end_offset = 0; + + // find parentheses and +address offset surrounding the mangled name: + // ./module(function+0x15c) [0x8048a6d] + for (char *p = symbollist[i]; *p; ++p) + { + if (*p == '(') + begin_name = p; + else if (*p == '+') + begin_offset = p; + else if (*p == ')' && begin_offset) + { + end_offset = p; + break; + } + } + + out << " 0x" << std::setfill('0') << std::setw(16) << std::hex << std::noshowbase << uint64_t(addrlist[i]); + + if (begin_name && begin_offset && end_offset && begin_name < begin_offset) + { + *begin_name++ = '\0'; + *begin_offset++ = '\0'; + *end_offset = '\0'; + + // mangled name is now in [begin_name, begin_offset) and caller + // offset in [begin_offset, end_offset). now apply + // __cxa_demangle(): + + int status; + char* ret = abi::__cxa_demangle(begin_name, funcname, &funcnamesize, &status); + if (status == 0) + { + funcname = ret; // use possibly realloc()-ed string + out << " " << symbollist[i] << " : " << funcname << "+" << begin_offset << std::endl; + } + else + { + // demangling failed. Output function name as a C function with + // no arguments. + out << " " << symbollist[i] << " : " << begin_name << "+" << begin_offset << std::endl; + } + } + else + { + // couldn't parse the line? print the whole line. + out << " " << symbollist[i] << std::endl; + } + } - print_stacktrace_linenums(addrlist, addrlen); + if( addr2line ) + print_stacktrace_linenums(addrlist, addrlen); - free(funcname); - free(symbollist); + free(funcname); + free(symbollist); } /* This structure mirrors the one found in /usr/include/asm/ucontext.h */ @@ -198,7 +198,7 @@ void segfault_handler(int sig_num, siginfo_t * info, void * ucontext) FC_UNUSED(caller_address); - print_stacktrace( std::cerr, 128, nullptr ); + print_stacktrace( std::cerr, 128, nullptr, true ); std::exit(EXIT_FAILURE); } @@ -231,11 +231,11 @@ namespace fc { void segfault_handler(int sig_num) { std::cerr << "caught segfault\n"; - print_stacktrace( std::cerr, 128, nullptr ); + print_stacktrace( std::cerr, 128, nullptr, false ); std::exit(EXIT_FAILURE); } -void print_stacktrace( std::ostream& out, unsigned int max_frames /* = 63 */, void* caller_overwrite_hack /* = nullptr */ ) +void print_stacktrace( std::ostream& out, unsigned int max_frames /* = 63 */, void* caller_overwrite_hack /* = nullptr */, bool addr2line /* = true */ ) { std::cerr << "print stacktrace\n"; assert( max_frames <= 128 ); @@ -275,9 +275,9 @@ void print_stacktrace_on_segfault() namespace fc { -void print_stacktrace(std::ostream& out, unsigned int max_frames /* = 63 */ ) +void print_stacktrace(std::ostream& out, unsigned int max_frames /* = 63 */, void* caller_overwrite_hack /* = nullptr */, bool addr2line /* = true */ ) { - out << "stack trace not supported on this compiler" << std::endl; + out << "stack trace not supported on this compiler" << std::endl; } void print_stacktrace_on_segfault() {} diff --git a/libraries/fc/src/time.cpp b/libraries/fc/src/time.cpp index d85a04dba8..99e93fbe08 100644 --- a/libraries/fc/src/time.cpp +++ b/libraries/fc/src/time.cpp @@ -49,7 +49,30 @@ namespace fc { time_point::operator fc::string()const { - return fc::string( time_point_sec( *this ) ); + return fc::string( time_point_sec( *this ) ); + } + + fc::string time_point::to_iso_string_in_seconds() const + { + return fc::string( time_point_sec( *this ) ); + } + + fc::string time_point::to_iso_string_in_milliseconds() const + { + std::ostringstream os; + os << (std::string)(*this); + uint64_t milliseconds = (time_since_epoch().count() % 1000000) / 1000; + os << "." << std::setw(3) << std::setfill('0') << milliseconds; + return os.str(); + } + + fc::string time_point::to_iso_string_in_microseconds() const + { + std::ostringstream os; + os << (std::string)(*this); + uint64_t microseconds = time_since_epoch().count() % 1000000; + os << "." << std::setw(6) << std::setfill('0') << microseconds; + return os.str(); } time_point time_point::from_iso_string( const fc::string& s ) diff --git a/libraries/fc/tests/crypto/base_n_tests.cpp b/libraries/fc/tests/crypto/base_n_tests.cpp index be7a72a1f6..8a0a6ff0dd 100644 --- a/libraries/fc/tests/crypto/base_n_tests.cpp +++ b/libraries/fc/tests/crypto/base_n_tests.cpp @@ -24,7 +24,7 @@ static void test_16( const std::string& test, const std::string& expected ) BOOST_CHECK_EQUAL( expected, enc2 ); char out[32]; - int len = fc::from_hex( enc1, out, 32 ); + unsigned int len = fc::from_hex( enc1, out, 32 ); BOOST_CHECK_EQUAL( test.size(), len ); BOOST_CHECK( !memcmp( test.c_str(), out, len ) ); if (len > 10) { diff --git a/libraries/fc/tests/crypto/blowfish_test.cpp b/libraries/fc/tests/crypto/blowfish_test.cpp index ec4ada7cce..48df9de29b 100644 --- a/libraries/fc/tests/crypto/blowfish_test.cpp +++ b/libraries/fc/tests/crypto/blowfish_test.cpp @@ -85,9 +85,9 @@ BOOST_AUTO_TEST_CASE(blowfish_ecb_test) { for ( int i = 0; i < 34; i++ ) { unsigned char key[8], plain[8], cipher[8], out[8]; - BOOST_CHECK_EQUAL( 8, fc::from_hex( ecb_tests[i].key, (char*) key, sizeof(key) ) ); - BOOST_CHECK_EQUAL( 8, fc::from_hex( ecb_tests[i].plain, (char*) plain, sizeof(plain) ) ); - BOOST_CHECK_EQUAL( 8, fc::from_hex( ecb_tests[i].cipher, (char*) cipher, sizeof(cipher) ) ); + BOOST_CHECK_EQUAL( 8u, fc::from_hex( ecb_tests[i].key, (char*) key, sizeof(key) ) ); + BOOST_CHECK_EQUAL( 8u, fc::from_hex( ecb_tests[i].plain, (char*) plain, sizeof(plain) ) ); + BOOST_CHECK_EQUAL( 8u, fc::from_hex( ecb_tests[i].cipher, (char*) cipher, sizeof(cipher) ) ); fc::blowfish fish; fish.start( key, 8 ); @@ -105,11 +105,11 @@ BOOST_AUTO_TEST_CASE(blowfish_ecb_test) BOOST_AUTO_TEST_CASE(blowfish_key_test) { unsigned char key[24], plain[8], cipher[8], out[8]; - BOOST_CHECK_EQUAL( 24, fc::from_hex( key_test_key.c_str(), (char*) key, sizeof(key) ) ); - BOOST_CHECK_EQUAL( 8, fc::from_hex( key_test_plain.c_str(), (char*) plain, sizeof(plain) ) ); + BOOST_CHECK_EQUAL( 24u, fc::from_hex( key_test_key.c_str(), (char*) key, sizeof(key) ) ); + BOOST_CHECK_EQUAL( 8u, fc::from_hex( key_test_plain.c_str(), (char*) plain, sizeof(plain) ) ); for ( unsigned int i = 0; i < sizeof(key); i++ ) { - BOOST_CHECK_EQUAL( 8, fc::from_hex( key_test_ciphers[i], (char*) cipher, sizeof(cipher) ) ); + BOOST_CHECK_EQUAL( 8u, fc::from_hex( key_test_ciphers[i], (char*) cipher, sizeof(cipher) ) ); fc::blowfish fish; fish.start( key, i + 1 ); fish.encrypt( plain, out, 8, fc::blowfish::ECB ); @@ -133,10 +133,10 @@ static unsigned int from_bytes( const unsigned char* p ) { BOOST_AUTO_TEST_CASE(blowfish_chain_test) { unsigned char key[16], iv[8], cipher[32], out[32]; - BOOST_CHECK_EQUAL( 16, fc::from_hex( chain_test_key.c_str(), (char*) key, sizeof(key) ) ); - BOOST_CHECK_EQUAL( 8, fc::from_hex( chain_test_iv.c_str(), (char*) iv, sizeof(iv) ) ); + BOOST_CHECK_EQUAL( 16u, fc::from_hex( chain_test_key.c_str(), (char*) key, sizeof(key) ) ); + BOOST_CHECK_EQUAL( 8u, fc::from_hex( chain_test_iv.c_str(), (char*) iv, sizeof(iv) ) ); - BOOST_CHECK_EQUAL( 32, fc::from_hex( chain_test_cbc.c_str(), (char*) cipher, sizeof(cipher) ) ); + BOOST_CHECK_EQUAL( 32u, fc::from_hex( chain_test_cbc.c_str(), (char*) cipher, sizeof(cipher) ) ); fc::blowfish fish; fish.start( key, sizeof(key), fc::sblock( from_bytes( iv ), from_bytes( iv + 4 ) ) ); fish.encrypt( (unsigned char*) chain_test_plain.c_str(), out, sizeof(out), fc::blowfish::CBC ); @@ -151,7 +151,7 @@ BOOST_AUTO_TEST_CASE(blowfish_chain_test) fish.decrypt( cipher, out, sizeof(cipher), fc::blowfish::CBC ); BOOST_CHECK( !memcmp( chain_test_plain.c_str(), out, 29 ) ); - BOOST_CHECK_EQUAL( 29, fc::from_hex( chain_test_cfb.c_str(), (char*) cipher, sizeof(cipher) ) ); + BOOST_CHECK_EQUAL( 29u, fc::from_hex( chain_test_cfb.c_str(), (char*) cipher, sizeof(cipher) ) ); fish.reset_chain(); fish.encrypt( (unsigned char*) chain_test_plain.c_str(), out, sizeof(out), fc::blowfish::CFB ); BOOST_CHECK( !memcmp( cipher, out, 29 ) ); diff --git a/libraries/fc/tests/hmac_test.cpp b/libraries/fc/tests/hmac_test.cpp index a8f21dd6d6..3b474d8252 100644 --- a/libraries/fc/tests/hmac_test.cpp +++ b/libraries/fc/tests/hmac_test.cpp @@ -72,7 +72,7 @@ static fc::hmac mac_224; static fc::hmac mac_256; static fc::hmac mac_512; -template +template static void run_test( const fc::string& key, const fc::string& data, const fc::string& expect_224, const fc::string& expect_256, const fc::string& expect_512 ) { diff --git a/libraries/fc/tests/real128_test.cpp b/libraries/fc/tests/real128_test.cpp index beae9b7e43..5020af9b2a 100644 --- a/libraries/fc/tests/real128_test.cpp +++ b/libraries/fc/tests/real128_test.cpp @@ -11,9 +11,9 @@ BOOST_AUTO_TEST_CASE(real128_test) { BOOST_CHECK_EQUAL(string(real128()), string("0.")); BOOST_CHECK_EQUAL(string(real128(0)), string("0.")); - BOOST_CHECK_EQUAL(real128(8).to_uint64(), 8); - BOOST_CHECK_EQUAL(real128(6789).to_uint64(), 6789); - BOOST_CHECK_EQUAL(real128(10000).to_uint64(), 10000); + BOOST_CHECK_EQUAL(real128(8).to_uint64(), 8u); + BOOST_CHECK_EQUAL(real128(6789).to_uint64(), 6789u); + BOOST_CHECK_EQUAL(real128(10000).to_uint64(), 10000u); BOOST_CHECK_EQUAL(string(real128(1)), string("1.")); BOOST_CHECK_EQUAL(string(real128(5)), string("5.")); BOOST_CHECK_EQUAL(string(real128(12345)), string("12345.")); @@ -41,8 +41,8 @@ BOOST_AUTO_TEST_CASE(real128_test) BOOST_CHECK_EQUAL( string(pi*1), "3.1415926535" ); BOOST_CHECK_EQUAL( string(pi*0), "0." ); - BOOST_CHECK_EQUAL(real128("12345.6789").to_uint64(), 12345); - BOOST_CHECK_EQUAL((real128("12345.6789")*10000).to_uint64(), 123456789); + BOOST_CHECK_EQUAL(real128("12345.6789").to_uint64(), 12345u); + BOOST_CHECK_EQUAL((real128("12345.6789")*10000).to_uint64(), 123456789u); BOOST_CHECK_EQUAL(string(real128("12345.6789")), string("12345.6789")); BOOST_CHECK_EQUAL( real128(uint64_t(-1)).to_uint64(), uint64_t(-1) ); diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/.gitignore b/libraries/fc/vendor/diff-match-patch-cpp-stl/.gitignore deleted file mode 100644 index 5e8ea059c8..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -# Compiled Object files -*.slo -*.lo -*.o - -# Compiled Dynamic libraries -*.so -*.dylib - -# Compiled Static libraries -*.lai -*.la -*.a -a.out -diff_match_patch_test -diff_match_patch_test_utf8 -CMakeLists.txt.user -speedtest diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/.travis.yml b/libraries/fc/vendor/diff-match-patch-cpp-stl/.travis.yml deleted file mode 100644 index 8254f784bd..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: cpp -compiler: - - gcc - - clang -before_script: - - mkdir build - - cd build - - cmake .. -script: make && ./diff_match_patch_test_string && ./diff_match_patch_test_wstring diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/CMakeLists.txt b/libraries/fc/vendor/diff-match-patch-cpp-stl/CMakeLists.txt deleted file mode 100644 index c33c2507ff..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -message("*** Build the test harness for C++ STL variants of Diff Match and Patch ***") -message("*** http://code.google.com/p/google-diff-match-patch/ and ***") -message("*** https://github.com/leutloff/diff-match-patch-cpp-stl ***") - -project (diff-match-patch-cpp-stl CXX) - -add_executable(diff_match_patch_test_wstring diff_match_patch_test_wstring.cpp) - -add_executable(diff_match_patch_test_string diff_match_patch_test_string.cpp) diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/LICENSE b/libraries/fc/vendor/diff-match-patch-cpp-stl/LICENSE deleted file mode 100644 index 37ec93a14f..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/README.md b/libraries/fc/vendor/diff-match-patch-cpp-stl/README.md deleted file mode 100644 index 222cbe4108..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/README.md +++ /dev/null @@ -1,21 +0,0 @@ -diff-match-patch-cpp-stl -======================== - -[![Build Status](https://travis-ci.org/leutloff/diff-match-patch-cpp-stl.png)](https://travis-ci.org/leutloff/diff-match-patch-cpp-stl) - -C++ STL variant of https://code.google.com/p/google-diff-match-patch. - -STL Port was done by Sergey Nozhenko (snhere@gmail.com) and posted on -https://code.google.com/p/google-diff-match-patch/issues/detail?id=25 - -The STL Port is header only and works with std::wstring and std::string. - -Compile and run the test cases: - - g++ diff_match_patch_test.cpp -o diff_match_patch_test && ./diff_match_patch_test - -Or use CMake ... - -Compile and run the speedtest - requires the files speedtest1.txt and speedtest2.txt for comparison: - - g++ speedtest.cpp -o speedtest && ./speedtest diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch.h b/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch.h deleted file mode 100644 index 47531c95ed..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch.h +++ /dev/null @@ -1,2596 +0,0 @@ -/* - * Copyright 2008 Google Inc. All Rights Reserved. - * Author: fraser@google.com (Neil Fraser) - * Author: mikeslemmer@gmail.com (Mike Slemmer) - * Author: snhere@gmail.com (Sergey Nozhenko) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Diff Match and Patch - * http://code.google.com/p/google-diff-match-patch/ - */ - -#ifndef DIFF_MATCH_PATCH_H -#define DIFF_MATCH_PATCH_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Functions for diff, match and patch. - * Computes the difference between two texts to create a patch. - * Applies the patch onto another text, allowing for errors. - * - * @author fraser@google.com (Neil Fraser) - * - * Qt/C++ port by mikeslemmer@gmail.com (Mike Slemmer) - * - * STL-only port by snhere@gmail.com (Sergey Nozhenko) - * and some tweaks for std::string by leutloff@sundancer.oche.de (Christian Leutloff) - * - * Here is a trivial sample program: - * - - #include "diff_match_patch.h" - #include - using namespace std; - int main(int argc, char **argv) { - diff_match_patch dmp; - wstring str1 = L"First string in diff"; - wstring str2 = L"Second string in diff"; - - wstring strPatch = dmp.patch_toText(dmp.patch_make(str1, str2)); - pair > out - = dmp.patch_apply(dmp.patch_fromText(strPatch), str1); - wstring strResult = out.first; - - // here, strResult will equal str2 above. - return 0; - } - - */ - -// Character type dependencies -template struct diff_match_patch_traits {}; - -/** - * Class containing the diff, match and patch methods. - * Also contains the behaviour settings. - */ -template > -class diff_match_patch { - public: - /** - * String and character types - */ - typedef stringT string_t; - typedef typename string_t::value_type char_t; - - /**- - * The data structure representing a diff is a Linked list of Diff objects: - * {Diff(Operation.DELETE, "Hello"), Diff(Operation.INSERT, "Goodbye"), - * Diff(Operation.EQUAL, " world.")} - * which means: delete "Hello", add "Goodbye" and keep " world." - */ - enum Operation { - DELETE, INSERT, EQUAL - }; - - /** - * Class representing one diff operation. - */ - class Diff { - public: - Operation operation; - // One of: INSERT, DELETE or EQUAL. - - string_t text; - // The text associated with this diff operation. - - /** - * Constructor. Initializes the diff with the provided values. - * @param operation One of INSERT, DELETE or EQUAL. - * @param text The text being applied. - */ - Diff(Operation _operation, const string_t &_text) : operation(_operation), text(_text) {} - Diff() {} - - /** - * Display a human-readable version of this Diff. - * @return text version. - */ - string_t toString() const { - string_t prettyText = text; - // Replace linebreaks with Pilcrow signs. - for (typename string_t::iterator i = prettyText.begin(); i != prettyText.end(); ++i) - if (traits::to_wchar(*i) == L'\n') *i = traits::from_wchar(L'\u00b6'); - return traits::cs(L"Diff(") + strOperation(operation) + traits::cs(L",\"") + prettyText + traits::cs(L"\")"); - } - - /** - * Is this Diff equivalent to another Diff? - * @param d Another Diff to compare against - * @return true or false - */ - bool operator==(const Diff &d) const { - return (d.operation == this->operation) && (d.text == this->text); - } - bool operator!=(const Diff &d) const { return !(operator == (d)); } - - static string_t strOperation(Operation op) { - switch (op) { - case INSERT: - return traits::cs(L"INSERT"); - case DELETE: - return traits::cs(L"DELETE"); - case EQUAL: - return traits::cs(L"EQUAL"); - } - throw string_t(traits::cs(L"Invalid operation.")); - } - }; - - typedef std::list Diffs; - - - /** - * Class representing one patch operation. - */ - class Patch { - public: - Diffs diffs; - int start1; - int start2; - int length1; - int length2; - - /** - * Constructor. Initializes with an empty list of diffs. - */ - Patch() : start1(0), start2(0), length1(0), length2(0) {} - - bool isNull() const { - return start1 == 0 && start2 == 0 && length1 == 0 && length2 == 0 && diffs.size() == 0; - } - - /** - * Emulate GNU diff's format. - * Header: @@ -382,8 +481,9 @@ - * Indices are printed as 1-based, not 0-based. - * @return The GNU diff string - */ - string_t toString() const { - string_t coords1, coords2; - if (length1 == 0) { - coords1 = to_string(start1) + traits::cs(L",0"); - } else if (length1 == 1) { - coords1 = to_string(start1 + 1); - } else { - coords1 = to_string(start1 + 1) + traits::from_wchar(L',') + to_string(length1); - } - if (length2 == 0) { - coords2 = to_string(start2) + traits::cs(L",0"); - } else if (length2 == 1) { - coords2 = to_string(start2 + 1); - } else { - coords2 = to_string(start2 + 1) + traits::from_wchar(L',') + to_string(length2); - } - string_t text(traits::cs(L"@@ -") + coords1 + traits::cs(L" +") + coords2 + traits::cs(L" @@\n")); - // Escape the body of the patch with %xx notation. - for (typename Diffs::const_iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - switch ((*cur_diff).operation) { - case INSERT: - text += traits::from_wchar(L'+'); - break; - case DELETE: - text += traits::from_wchar(L'-'); - break; - case EQUAL: - text += traits::from_wchar(L' '); - break; - } - append_percent_encoded(text, (*cur_diff).text); - text += traits::from_wchar(L'\n'); - } - - return text; - } - }; - - typedef std::list Patches; - - friend class diff_match_patch_test; - - public: - // Defaults. - // Set these on your diff_match_patch instance to override the defaults. - - // Number of seconds to map a diff before giving up (0 for infinity). - float Diff_Timeout; - // Cost of an empty edit operation in terms of edit characters. - short Diff_EditCost; - // At what point is no match declared (0.0 = perfection, 1.0 = very loose). - float Match_Threshold; - // How far to search for a match (0 = exact location, 1000+ = broad match). - // A match this many characters away from the expected location will add - // 1.0 to the score (0.0 is a perfect match). - int Match_Distance; - // When deleting a large block of text (over ~64 characters), how close does - // the contents have to match the expected contents. (0.0 = perfection, - // 1.0 = very loose). Note that Match_Threshold controls how closely the - // end points of a delete need to match. - float Patch_DeleteThreshold; - // Chunk size for context length. - short Patch_Margin; - - // The number of bits in an int. - short Match_MaxBits; - - - public: - - diff_match_patch() : - Diff_Timeout(1.0f), - Diff_EditCost(4), - Match_Threshold(0.5f), - Match_Distance(1000), - Patch_DeleteThreshold(0.5f), - Patch_Margin(4), - Match_MaxBits(32) { - } - - // DIFF FUNCTIONS - - /** - * Find the differences between two texts. - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster slightly less optimal diff. - * Most of the time checklines is wanted, so default to true. - * @return Linked List of Diff objects. - */ - Diffs diff_main(const string_t &text1, const string_t &text2, bool checklines = true) const { - // Set a deadline by which time the diff must be complete. - clock_t deadline; - if (Diff_Timeout <= 0) { - deadline = std::numeric_limits::max(); - } else { - deadline = clock() + (clock_t)(Diff_Timeout * CLOCKS_PER_SEC); - } - Diffs diffs; - diff_main(text1, text2, checklines, deadline, diffs); - return diffs; - } - - /** - * Find the differences between two texts. Simplifies the problem by - * stripping any common prefix or suffix off the texts before diffing. - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster slightly less optimal diff. - * @param deadline Time when the diff should be complete by. Used - * internally for recursive calls. Users should set DiffTimeout instead. - * @param Linked List of Diff objects. - */ - private: - static void diff_main(const string_t &text1, const string_t &text2, bool checklines, clock_t deadline, Diffs& diffs) { - diffs.clear(); - - // Check for equality (speedup). - if (text1 == text2) { - if (!text1.empty()) { - diffs.push_back(Diff(EQUAL, text1)); - } - } - else { - // Trim off common prefix (speedup). - int commonlength = diff_commonPrefix(text1, text2); - const string_t &commonprefix = text1.substr(0, commonlength); - string_t textChopped1 = text1.substr(commonlength); - string_t textChopped2 = text2.substr(commonlength); - - // Trim off common suffix (speedup). - commonlength = diff_commonSuffix(textChopped1, textChopped2); - const string_t &commonsuffix = right(textChopped1, commonlength); - textChopped1 = textChopped1.substr(0, textChopped1.length() - commonlength); - textChopped2 = textChopped2.substr(0, textChopped2.length() - commonlength); - - // Compute the diff on the middle block. - diff_compute(textChopped1, textChopped2, checklines, deadline, diffs); - - // Restore the prefix and suffix. - if (!commonprefix.empty()) { - diffs.push_front(Diff(EQUAL, commonprefix)); - } - if (!commonsuffix.empty()) { - diffs.push_back(Diff(EQUAL, commonsuffix)); - } - - diff_cleanupMerge(diffs); - } - } - - /** - * Find the differences between two texts. Assumes that the texts do not - * have any common prefix or suffix. - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster slightly less optimal diff. - * @param deadline Time when the diff should be complete by. - * @param Linked List of Diff objects. - */ - private: - static void diff_compute(string_t text1, string_t text2, bool checklines, clock_t deadline, Diffs& diffs) { - if (text1.empty()) { - // Just add some text (speedup). - diffs.push_back(Diff(INSERT, text2)); - return; - } - - if (text2.empty()) { - // Just delete some text (speedup). - diffs.push_back(Diff(DELETE, text1)); - return; - } - - { - const string_t& longtext = text1.length() > text2.length() ? text1 : text2; - const string_t& shorttext = text1.length() > text2.length() ? text2 : text1; - const size_t i = longtext.find(shorttext); - if (i != string_t::npos) { - // Shorter text is inside the longer text (speedup). - const Operation op = (text1.length() > text2.length()) ? DELETE : INSERT; - diffs.push_back(Diff(op, longtext.substr(0, i))); - diffs.push_back(Diff(EQUAL, shorttext)); - diffs.push_back(Diff(op, safeMid(longtext, i + shorttext.length()))); - return; - } - - if (shorttext.length() == 1) { - // Single character string. - // After the previous speedup, the character can't be an equality. - diffs.push_back(Diff(DELETE, text1)); - diffs.push_back(Diff(INSERT, text2)); - return; - } - // Garbage collect longtext and shorttext by scoping out. - } - - // Don't risk returning a non-optimal diff if we have unlimited time. - if (deadline != std::numeric_limits::max()) { - // Check to see if the problem can be split in two. - HalfMatchResult hm; - if (diff_halfMatch(text1, text2, hm)) { - // A half-match was found, sort out the return data. - // Send both pairs off for separate processing. - diff_main(hm.text1_a, hm.text2_a, checklines, deadline, diffs); - diffs.push_back(Diff(EQUAL, hm.mid_common)); - Diffs diffs_b; - diff_main(hm.text1_b, hm.text2_b, checklines, deadline, diffs_b); - diffs.splice(diffs.end(), diffs_b); - return; - } - } - - // Perform a real diff. - if (checklines && text1.length() > 100 && text2.length() > 100) { - diff_lineMode(text1, text2, deadline, diffs); - return; - } - - diff_bisect(text1, text2, deadline, diffs); - } - - /** - * Do a quick line-level diff on both strings, then rediff the parts for - * greater accuracy. - * This speedup can produce non-minimal diffs. - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param deadline Time when the diff should be complete by. - * @param Linked List of Diff objects. - */ - private: - static void diff_lineMode(string_t text1, string_t text2, clock_t deadline, Diffs& diffs) { - // Scan the text on a line-by-line basis first. - Lines linearray; - diff_linesToChars(text1, text2, linearray); - - diff_main(text1, text2, false, deadline, diffs); - - // Convert the diff back to original text. - diff_charsToLines(diffs, linearray); - // Eliminate freak matches (e.g. blank lines) - diff_cleanupSemantic(diffs); - - // Rediff any replacement blocks, this time character-by-character. - // Add a dummy entry at the end. - diffs.push_back(Diff(EQUAL, string_t())); - int count_delete = 0; - int count_insert = 0; - string_t text_delete; - string_t text_insert; - - for (typename Diffs::iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - switch ((*cur_diff).operation) { - case INSERT: - count_insert++; - text_insert += (*cur_diff).text; - break; - case DELETE: - count_delete++; - text_delete += (*cur_diff).text; - break; - case EQUAL: - // Upon reaching an equality, check for prior redundancies. - if (count_delete >= 1 && count_insert >= 1) { - // Delete the offending records and add the merged ones. - typename Diffs::iterator last = cur_diff; - std::advance(cur_diff, -(count_delete + count_insert)); - cur_diff = diffs.erase(cur_diff, last); - - Diffs new_diffs; - diff_main(text_delete, text_insert, false, deadline, new_diffs); - diffs.splice(cur_diff++, new_diffs); - --cur_diff; - } - count_insert = 0; - count_delete = 0; - text_delete.clear(); - text_insert.clear(); - break; - } - } - diffs.pop_back(); // Remove the dummy entry at the end. - } - - /** - * Find the 'middle snake' of a diff, split the problem in two - * and return the recursively constructed diff. - * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @return Linked List of Diff objects. - */ - protected: - static Diffs diff_bisect(const string_t &text1, const string_t &text2, clock_t deadline) { - Diffs diffs; - diff_bisect(text1, text2, deadline, diffs); - return diffs; - } - private: - static void diff_bisect(const string_t &text1, const string_t &text2, clock_t deadline, Diffs& diffs) { - // Cache the text lengths to prevent multiple calls. - const int text1_length = text1.length(); - const int text2_length = text2.length(); - const int max_d = (text1_length + text2_length + 1) / 2; - const int v_offset = max_d; - const int v_length = 2 * max_d; - std::vector v1(v_length, -1), - v2(v_length, -1); - v1[v_offset + 1] = 0; - v2[v_offset + 1] = 0; - const int delta = text1_length - text2_length; - // If the total number of characters is odd, then the front path will - // collide with the reverse path. - const bool front = (delta % 2 != 0); - // Offsets for start and end of k loop. - // Prevents mapping of space beyond the grid. - int k1start = 0; - int k1end = 0; - int k2start = 0; - int k2end = 0; - for (int d = 0; d < max_d; d++) { - // Bail out if deadline is reached. - if (clock() > deadline) { - break; - } - - // Walk the front path one step. - for (int k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { - const int k1_offset = v_offset + k1; - int x1; - if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { - x1 = v1[k1_offset + 1]; - } else { - x1 = v1[k1_offset - 1] + 1; - } - int y1 = x1 - k1; - while (x1 < text1_length && y1 < text2_length - && text1[x1] == text2[y1]) { - x1++; - y1++; - } - v1[k1_offset] = x1; - if (x1 > text1_length) { - // Ran off the right of the graph. - k1end += 2; - } else if (y1 > text2_length) { - // Ran off the bottom of the graph. - k1start += 2; - } else if (front) { - int k2_offset = v_offset + delta - k1; - if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { - // Mirror x2 onto top-left coordinate system. - int x2 = text1_length - v2[k2_offset]; - if (x1 >= x2) { - // Overlap detected. - diff_bisectSplit(text1, text2, x1, y1, deadline, diffs); - return; - } - } - } - } - - // Walk the reverse path one step. - for (int k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { - const int k2_offset = v_offset + k2; - int x2; - if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { - x2 = v2[k2_offset + 1]; - } else { - x2 = v2[k2_offset - 1] + 1; - } - int y2 = x2 - k2; - while (x2 < text1_length && y2 < text2_length - && text1[text1_length - x2 - 1] == text2[text2_length - y2 - 1]) { - x2++; - y2++; - } - v2[k2_offset] = x2; - if (x2 > text1_length) { - // Ran off the left of the graph. - k2end += 2; - } else if (y2 > text2_length) { - // Ran off the top of the graph. - k2start += 2; - } else if (!front) { - int k1_offset = v_offset + delta - k2; - if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { - int x1 = v1[k1_offset]; - int y1 = v_offset + x1 - k1_offset; - // Mirror x2 onto top-left coordinate system. - x2 = text1_length - x2; - if (x1 >= x2) { - // Overlap detected. - diff_bisectSplit(text1, text2, x1, y1, deadline, diffs); - return; - } - } - } - } - } - // Diff took too long and hit the deadline or - // number of diffs equals number of characters, no commonality at all. - diffs.clear(); - diffs.push_back(Diff(DELETE, text1)); - diffs.push_back(Diff(INSERT, text2)); - } - - /** - * Given the location of the 'middle snake', split the diff in two parts - * and recurse. - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param x Index of split point in text1. - * @param y Index of split point in text2. - * @param deadline Time at which to bail if not yet complete. - * @return LinkedList of Diff objects. - */ - private: - static void diff_bisectSplit(const string_t &text1, const string_t &text2, int x, int y, clock_t deadline, Diffs& diffs) { - string_t text1a = text1.substr(0, x); - string_t text2a = text2.substr(0, y); - string_t text1b = safeMid(text1, x); - string_t text2b = safeMid(text2, y); - - // Compute both diffs serially. - diff_main(text1a, text2a, false, deadline, diffs); - Diffs diffs_b; - diff_main(text1b, text2b, false, deadline, diffs_b); - diffs.splice(diffs.end(), diffs_b); - } - - /** - * Split two texts into a list of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * @param text1 First string. - * @param text2 Second string. - * @return Lines object, containing the encoded text1, the - * encoded text2 and the List of pointers to unique strings. The zeroth element - * of the List of unique strings is intentionally blank. - */ - protected: - struct LinePtr : std::pair { - LinePtr() {} - LinePtr(typename string_t::const_pointer p, size_t n) : std::pair(p, n) {} - bool operator<(const LinePtr& p) const - { return this->second < p.second? true : this->second > p.second? false : string_t::traits_type::compare(this->first, p.first, this->second) < 0; } - }; - struct Lines : std::vector { string_t text1, text2; }; - - static void diff_linesToChars(string_t &text1, string_t &text2, Lines& lineArray) { - std::map lineHash; - lineArray.text1.swap(text1), lineArray.text2.swap(text2); - // e.g. linearray[4] == "Hello\n" - // e.g. linehash.get("Hello\n") == 4 - - // "\x00" is a valid character, but various debuggers don't like it. - // So we'll insert a junk entry to avoid generating a null character. - - text1 = diff_linesToCharsMunge(lineArray.text1, lineHash); - text2 = diff_linesToCharsMunge(lineArray.text2, lineHash); - - lineArray.resize(lineHash.size() + 1); - for (typename std::map::const_iterator i = lineHash.begin(); i != lineHash.end(); ++i) - lineArray[(*i).second] = (*i).first; - } - - /** - * Split a text into a list of pointers to strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * @param text String to encode. - * @param lineHash Map of string pointers to indices. - * @return Encoded string. - */ - private: - static string_t diff_linesToCharsMunge(const string_t &text, std::map &lineHash) { - string_t chars; - // Walk the text, pulling out a substring for each line. - // text.split('\n') would would temporarily double our memory footprint. - // Modifying text would create many large strings to garbage collect. - typename string_t::size_type lineLen; - for (typename string_t::const_pointer lineStart = text.c_str(), textEnd = lineStart + text.size(); lineStart < textEnd; lineStart += lineLen + 1) { - lineLen = next_token(text, traits::from_wchar(L'\n'), lineStart); - if (lineStart + lineLen == textEnd) --lineLen; - chars += (char_t)(*lineHash.insert(std::make_pair(LinePtr(lineStart, lineLen + 1), lineHash.size() + 1)).first).second; - } - return chars; - } - - /** - * Rehydrate the text in a diff from a string of line hashes to real lines of - * text. - * @param diffs LinkedList of Diff objects. - * @param lineArray List of pointers to unique strings. - */ - private: - static void diff_charsToLines(Diffs &diffs, const Lines& lineArray) { - for (typename Diffs::iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - string_t text; - for (int y = 0; y < (int)(*cur_diff).text.length(); y++) { - const LinePtr& lp = lineArray[static_cast((*cur_diff).text[y])]; - text.append(lp.first, lp.second); - } - (*cur_diff).text.swap(text); - } - } - - /** - * Determine the common prefix of two strings. - * @param text1 First string. - * @param text2 Second string. - * @return The number of characters common to the start of each string. - */ - public: - static int diff_commonPrefix(const string_t &text1, const string_t &text2) { - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - const int n = std::min(text1.length(), text2.length()); - for (int i = 0; i < n; i++) { - if (text1[i] != text2[i]) { - return i; - } - } - return n; - } - - /** - * Determine the common suffix of two strings. - * @param text1 First string. - * @param text2 Second string. - * @return The number of characters common to the end of each string. - */ - public: - static int diff_commonSuffix(const string_t &text1, const string_t &text2) { - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - const int text1_length = text1.length(); - const int text2_length = text2.length(); - const int n = std::min(text1_length, text2_length); - for (int i = 1; i <= n; i++) { - if (text1[text1_length - i] != text2[text2_length - i]) { - return i - 1; - } - } - return n; - } - - /** - * Determine if the suffix of one string is the prefix of another. - * @param text1 First string. - * @param text2 Second string. - * @return The number of characters common to the end of the first - * string and the start of the second string. - */ - protected: - static int diff_commonOverlap(const string_t &text1, const string_t &text2) { - // Cache the text lengths to prevent multiple calls. - const int text1_length = text1.length(); - const int text2_length = text2.length(); - // Eliminate the null case. - if (text1_length == 0 || text2_length == 0) { - return 0; - } - // Truncate the longer string. - string_t text1_trunc = text1; - string_t text2_trunc = text2; - if (text1_length > text2_length) { - text1_trunc = right(text1, text2_length); - } else if (text1_length < text2_length) { - text2_trunc = text2.substr(0, text1_length); - } - const int text_length = std::min(text1_length, text2_length); - // Quick check for the worst case. - if (text1_trunc == text2_trunc) { - return text_length; - } - - // Start by looking for a single character match - // and increase length until no match is found. - // Performance analysis: http://neil.fraser.name/news/2010/11/04/ - int best = 0; - int length = 1; - while (true) { - string_t pattern = right(text1_trunc, length); - size_t found = text2_trunc.find(pattern); - if (found == string_t::npos) { - return best; - } - length += found; - if (found == 0 || right(text1_trunc, length) == text2_trunc.substr(0, length)) { - best = length; - length++; - } - } - } - - /** - * Do the two texts share a substring which is at least half the length of - * the longer text? - * This speedup can produce non-minimal diffs. - * @param text1 First string. - * @param text2 Second string. - * @param HalfMatchResult object, containing the prefix of text1, the - * suffix of text1, the prefix of text2, the suffix of text2 and the - * common middle. - * @return Boolean true if there was a match, false otherwise. - */ - protected: - struct HalfMatchResult { - string_t text1_a, text1_b, text2_a, text2_b, mid_common; - void swap(HalfMatchResult& hm) { - text1_a.swap(hm.text1_a), text1_b.swap(hm.text1_b), text2_a.swap(hm.text2_a), text2_b.swap(hm.text2_b), mid_common.swap(hm.mid_common); - } - }; - - static bool diff_halfMatch(const string_t &text1, const string_t &text2, HalfMatchResult& hm) { - const string_t longtext = text1.length() > text2.length() ? text1 : text2; - const string_t shorttext = text1.length() > text2.length() ? text2 : text1; - if (longtext.length() < 4 || shorttext.length() * 2 < longtext.length()) { - return false; // Pointless. - } - - HalfMatchResult res1, res2; - // First check if the second quarter is the seed for a half-match. - bool hm1 = diff_halfMatchI(longtext, shorttext, - (longtext.length() + 3) / 4, res1); - // Check again based on the third quarter. - bool hm2 = diff_halfMatchI(longtext, shorttext, - (longtext.length() + 1) / 2, res2); - if (!hm1 && !hm2) { - return false; - } else if (!hm2) { - hm.swap(res1); - } else if (!hm1) { - hm.swap(res2); - } else { - // Both matched. Select the longest. - hm.swap(res1.mid_common.length() > res2.mid_common.length() ? res1 : res2); - } - - // A half-match was found, sort out the return data. - if (text1.length() <= text2.length()) { - hm.text1_a.swap(hm.text2_a); - hm.text1_b.swap(hm.text2_b); - } - return true; - } - - /** - * Does a substring of shorttext exist within longtext such that the - * substring is at least half the length of longtext? - * @param longtext Longer string. - * @param shorttext Shorter string. - * @param i Start index of quarter length substring within longtext. - * @param HalfMatchResult object, containing the prefix of longtext, the - * suffix of longtext, the prefix of shorttext, the suffix of shorttext - * and the common middle. - * @return Boolean true if there was a match, false otherwise. - */ - private: - static bool diff_halfMatchI(const string_t &longtext, const string_t &shorttext, int i, HalfMatchResult& best) { - // Start with a 1/4 length substring at position i as a seed. - const string_t seed = safeMid(longtext, i, longtext.length() / 4); - size_t j = string_t::npos; - while ((j = shorttext.find(seed, j + 1)) != string_t::npos) { - const int prefixLength = diff_commonPrefix(safeMid(longtext, i), - safeMid(shorttext, j)); - const int suffixLength = diff_commonSuffix(longtext.substr(0, i), - shorttext.substr(0, j)); - if ((int)best.mid_common.length() < suffixLength + prefixLength) { - best.mid_common = safeMid(shorttext, j - suffixLength, suffixLength) - + safeMid(shorttext, j, prefixLength); - best.text1_a = longtext.substr(0, i - suffixLength); - best.text1_b = safeMid(longtext, i + prefixLength); - best.text2_a = shorttext.substr(0, j - suffixLength); - best.text2_b = safeMid(shorttext, j + prefixLength); - } - } - return best.mid_common.length() * 2 >= longtext.length(); - } - - /** - * Reduce the number of edits by eliminating semantically trivial equalities. - * @param diffs LinkedList of Diff objects. - */ - public: - static void diff_cleanupSemantic(Diffs &diffs) { - if (diffs.empty()) { - return; - } - bool changes = false; - std::vector equalities; // Stack of equalities. - string_t lastequality; // Always equal to equalities.lastElement().text - typename Diffs::iterator cur_diff; - // Number of characters that changed prior to the equality. - int length_insertions1 = 0; - int length_deletions1 = 0; - // Number of characters that changed after the equality. - int length_insertions2 = 0; - int length_deletions2 = 0; - for (cur_diff = diffs.begin(); cur_diff != diffs.end();) { - if ((*cur_diff).operation == EQUAL) { - // Equality found. - equalities.push_back(cur_diff); - length_insertions1 = length_insertions2; - length_deletions1 = length_deletions2; - length_insertions2 = 0; - length_deletions2 = 0; - lastequality = (*cur_diff).text; - } else { - // An insertion or deletion. - if ((*cur_diff).operation == INSERT) { - length_insertions2 += (*cur_diff).text.length(); - } else { - length_deletions2 += (*cur_diff).text.length(); - } - // Eliminate an equality that is smaller or equal to the edits on both - // sides of it. - if (!lastequality.empty() - && ((int)lastequality.length() - <= std::max(length_insertions1, length_deletions1)) - && ((int)lastequality.length() - <= std::max(length_insertions2, length_deletions2))) { - // printf("Splitting: '%s'\n", qPrintable(lastequality)); - // Walk back to offending equality. - // Change second copy to insert. - (*(cur_diff = equalities.back())).operation = INSERT; - // Duplicate record. - diffs.insert(cur_diff, Diff(DELETE, lastequality)); - equalities.pop_back(); // Throw away the equality we just deleted. - if (!equalities.empty()) { - // Throw away the previous equality (it needs to be reevaluated). - equalities.pop_back(); - } - length_insertions1 = 0; // Reset the counters. - length_deletions1 = 0; - length_insertions2 = 0; - length_deletions2 = 0; - lastequality = string_t(); - changes = true; - - if (!equalities.empty()) - // There is a safe equality we can fall back to. - cur_diff = equalities.back(); - else - { - // There are no previous equalities, walk back to the start. - cur_diff = diffs.begin(); - continue; - } - } - } - ++cur_diff; - } - - // Normalize the diff. - if (changes) { - diff_cleanupMerge(diffs); - } - diff_cleanupSemanticLossless(diffs); - - // Find any overlaps between deletions and insertions. - // e.g: abcxxxxxxdef - // -> abcxxxdef - // e.g: xxxabcdefxxx - // -> defxxxabc - // Only extract an overlap if it is as big as the edit ahead or behind it. - if ((cur_diff = diffs.begin()) != diffs.end()) { - for (typename Diffs::iterator prev_diff = cur_diff; ++cur_diff != diffs.end(); prev_diff = cur_diff) { - if ((*prev_diff).operation == DELETE && - (*cur_diff).operation == INSERT) { - string_t deletion = (*prev_diff).text; - string_t insertion = (*cur_diff).text; - int overlap_length1 = diff_commonOverlap(deletion, insertion); - int overlap_length2 = diff_commonOverlap(insertion, deletion); - if (overlap_length1 >= overlap_length2) { - if (overlap_length1 >= deletion.size() / 2.0 || - overlap_length1 >= insertion.size() / 2.0) { - // Overlap found. Insert an equality and trim the surrounding edits. - diffs.insert(cur_diff, Diff(EQUAL, insertion.substr(0, overlap_length1))); - prev_diff->text = - deletion.substr(0, deletion.length() - overlap_length1); - cur_diff->text = safeMid(insertion, overlap_length1); - // diffs.insert inserts the element before the cursor, so there is - // no need to step past the new element. - } - } else { - if (overlap_length2 >= deletion.length() / 2.0 || - overlap_length2 >= insertion.length() / 2.0) { - // Reverse overlap found. - // Insert an equality and swap and trim the surrounding edits. - diffs.insert(cur_diff, Diff(EQUAL, deletion.substr(0, overlap_length2))); - prev_diff->operation = INSERT; - prev_diff->text = - insertion.substr(0, insertion.length() - overlap_length2); - cur_diff->operation = DELETE; - cur_diff->text = safeMid(deletion, overlap_length2); - // diffs.insert inserts the element before the cursor, so there is - // no need to step past the new element. - } - } - if (++cur_diff == diffs.end()) break; - } - } - } - } - - /** - * Look for single edits surrounded on both sides by equalities - * which can be shifted sideways to align the edit to a word boundary. - * e.g: The cat came. -> The cat came. - * @param diffs LinkedList of Diff objects. - */ - public: - static void diff_cleanupSemanticLossless(Diffs &diffs) { - string_t equality1, edit, equality2; - string_t commonString; - int commonOffset; - int score, bestScore; - string_t bestEquality1, bestEdit, bestEquality2; - // Create a new iterator at the start. - typename Diffs::iterator prev_diff = diffs.begin(), cur_diff = prev_diff; - if (prev_diff == diffs.end() || ++cur_diff == diffs.end()) return; - - // Intentionally ignore the first and last element (don't need checking). - for (typename Diffs::iterator next_diff = cur_diff; ++next_diff != diffs.end(); prev_diff = cur_diff, cur_diff = next_diff) { - if ((*prev_diff).operation == EQUAL && - (*next_diff).operation == EQUAL) { - // This is a single edit surrounded by equalities. - equality1 = (*prev_diff).text; - edit = (*cur_diff).text; - equality2 = (*next_diff).text; - - // First, shift the edit as far left as possible. - commonOffset = diff_commonSuffix(equality1, edit); - if (commonOffset != 0) { - commonString = safeMid(edit, edit.length() - commonOffset); - equality1 = equality1.substr(0, equality1.length() - commonOffset); - edit = commonString + edit.substr(0, edit.length() - commonOffset); - equality2 = commonString + equality2; - } - - // Second, step character by character right, looking for the best fit. - bestEquality1 = equality1; - bestEdit = edit; - bestEquality2 = equality2; - bestScore = diff_cleanupSemanticScore(equality1, edit) - + diff_cleanupSemanticScore(edit, equality2); - while (!edit.empty() && !equality2.empty() - && edit[0] == equality2[0]) { - equality1 += edit[0]; - edit = safeMid(edit, 1) + equality2[0]; - equality2 = safeMid(equality2, 1); - score = diff_cleanupSemanticScore(equality1, edit) - + diff_cleanupSemanticScore(edit, equality2); - // The >= encourages trailing rather than leading whitespace on edits. - if (score >= bestScore) { - bestScore = score; - bestEquality1 = equality1; - bestEdit = edit; - bestEquality2 = equality2; - } - } - - if ((*prev_diff).text != bestEquality1) { - // We have an improvement, save it back to the diff. - if (!bestEquality1.empty()) { - (*prev_diff).text = bestEquality1; - } else { - diffs.erase(prev_diff); - } - (*cur_diff).text = bestEdit; - if (!bestEquality2.empty()) { - (*next_diff).text = bestEquality2; - } else { - diffs.erase(next_diff); // Delete nextDiff. - next_diff = cur_diff; - cur_diff = prev_diff; - } - } - } - } - } - - /** - * Given two strings, compute a score representing whether the internal - * boundary falls on logical boundaries. - * Scores range from 6 (best) to 0 (worst). - * @param one First string. - * @param two Second string. - * @return The score. - */ - private: - static int diff_cleanupSemanticScore(const string_t &one, const string_t &two) { - if (one.empty() || two.empty()) { - // Edges are the best. - return 6; - } - - // Each port of this function behaves slightly differently due to - // subtle differences in each language's definition of things like - // 'whitespace'. Since this function's purpose is largely cosmetic, - // the choice has been made to use each language's native features - // rather than force total conformity. - char_t char1 = one[one.length() - 1]; - char_t char2 = two[0]; - bool nonAlphaNumeric1 = !traits::is_alnum(char1); - bool nonAlphaNumeric2 = !traits::is_alnum(char2); - bool whitespace1 = nonAlphaNumeric1 && traits::is_space(char1); - bool whitespace2 = nonAlphaNumeric2 && traits::is_space(char2); - bool lineBreak1 = whitespace1 && is_control(char1); - bool lineBreak2 = whitespace2 && is_control(char2); - bool blankLine1 = false; - if (lineBreak1) { - typename string_t::const_reverse_iterator p1 = one.rbegin(), p2 = one.rend(); - if (traits::to_wchar(*p1) == L'\n' && ++p1 != p2) { - if (traits::to_wchar(*p1) == L'\r') - ++p1; - blankLine1 = p1 != p2 && traits::to_wchar(*p1) == L'\n'; - } - } - bool blankLine2 = false; - if (lineBreak2) { - typename string_t::const_iterator p1 = two.end(), p2 = two.begin(); - if (traits::to_wchar(*p2) == L'\r') - ++p2; - if (p2 != p1 && traits::to_wchar(*p2) == L'\n') { - if (++p2 != p1 && traits::to_wchar(*p2) == L'\r') - ++p2; - if (p2 != p1 && traits::to_wchar(*p2) == L'\n') - blankLine2 = true; - } - } - - if (blankLine1 || blankLine2) { - // Five points for blank lines. - return 5; - } else if (lineBreak1 || lineBreak2) { - // Four points for line breaks. - return 4; - } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { - // Three points for end of sentences. - return 3; - } else if (whitespace1 || whitespace2) { - // Two points for whitespace. - return 2; - } else if (nonAlphaNumeric1 || nonAlphaNumeric2) { - // One point for non-alphanumeric. - return 1; - } - return 0; - } - /** - * Reduce the number of edits by eliminating operationally trivial equalities. - * @param diffs LinkedList of Diff objects. - */ - public: - void diff_cleanupEfficiency(Diffs &diffs) const { - if (diffs.empty()) { - return; - } - bool changes = false; - std::vector equalities; // Stack of equalities. - string_t lastequality; // Always equal to equalities.lastElement().text - // Is there an insertion operation before the last equality. - bool pre_ins = false; - // Is there a deletion operation before the last equality. - bool pre_del = false; - // Is there an insertion operation after the last equality. - bool post_ins = false; - // Is there a deletion operation after the last equality. - bool post_del = false; - - for (typename Diffs::iterator cur_diff = diffs.begin(); cur_diff != diffs.end();) { - if ((*cur_diff).operation == EQUAL) { - // Equality found. - if ((int)(*cur_diff).text.length() < Diff_EditCost && (post_ins || post_del)) { - // Candidate found. - equalities.push_back(cur_diff); - pre_ins = post_ins; - pre_del = post_del; - lastequality = (*cur_diff).text; - } else { - // Not a candidate, and can never become one. - equalities.clear(); - lastequality.clear(); - } - post_ins = post_del = false; - } else { - // An insertion or deletion. - if ((*cur_diff).operation == DELETE) { - post_del = true; - } else { - post_ins = true; - } - /* - * Five types to be split: - * ABXYCD - * AXCD - * ABXC - * AXCD - * ABXC - */ - if (!lastequality.empty() - && ((pre_ins && pre_del && post_ins && post_del) - || (((int)lastequality.length() < Diff_EditCost / 2) - && ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0) - + (post_ins ? 1 : 0) + (post_del ? 1 : 0)) == 3))) { - // printf("Splitting: '%s'\n", qPrintable(lastequality)); - // Walk back to offending equality. - // Change second copy to insert. - (*(cur_diff = equalities.back())).operation = INSERT; - // Duplicate record. - diffs.insert(cur_diff, Diff(DELETE, lastequality)); - equalities.pop_back(); // Throw away the equality we just deleted. - lastequality.clear(); - changes = true; - if (pre_ins && pre_del) { - // No changes made which could affect previous entry, keep going. - post_ins = post_del = true; - equalities.clear(); - } else { - if (!equalities.empty()) { - // Throw away the previous equality (it needs to be reevaluated). - equalities.pop_back(); - } - post_ins = post_del = false; - if (!equalities.empty()) - // There is a safe equality we can fall back to. - cur_diff = equalities.back(); - else - { - // There are no previous equalities, walk back to the start. - cur_diff = diffs.begin(); - continue; - } - } - } - } - ++cur_diff; - } - - if (changes) { - diff_cleanupMerge(diffs); - } - } - - /** - * Reorder and merge like edit sections. Merge equalities. - * Any edit section can move as long as it doesn't cross an equality. - * @param diffs LinkedList of Diff objects. - */ - public: - static void diff_cleanupMerge(Diffs &diffs) { - diffs.push_back(Diff(EQUAL, string_t())); // Add a dummy entry at the end. - typename Diffs::iterator prev_diff, cur_diff; - int count_delete = 0; - int count_insert = 0; - string_t text_delete; - string_t text_insert; - Diff *prevEqual = NULL; - int commonlength; - for (cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - switch ((*cur_diff).operation) { - case INSERT: - count_insert++; - text_insert += (*cur_diff).text; - prevEqual = NULL; - break; - case DELETE: - count_delete++; - text_delete += (*cur_diff).text; - prevEqual = NULL; - break; - case EQUAL: - if (count_delete + count_insert > 1) { - // Delete the offending records. - prev_diff = cur_diff; - std::advance(prev_diff, -(count_delete + count_insert)); - diffs.erase(prev_diff, cur_diff); - if (count_delete != 0 && count_insert != 0) { - // Factor out any common prefixes. - commonlength = diff_commonPrefix(text_insert, text_delete); - if (commonlength != 0) { - if (cur_diff != diffs.begin()) { - prev_diff = cur_diff; - if ((*--prev_diff).operation != EQUAL) { - throw string_t(traits::cs(L"Previous diff should have been an equality.")); - } - (*prev_diff).text += text_insert.substr(0, commonlength); - } else { - diffs.insert(cur_diff, Diff(EQUAL, text_insert.substr(0, commonlength))); - } - text_insert = safeMid(text_insert, commonlength); - text_delete = safeMid(text_delete, commonlength); - } - // Factor out any common suffixes. - commonlength = diff_commonSuffix(text_insert, text_delete); - if (commonlength != 0) { - (*cur_diff).text = safeMid(text_insert, text_insert.length() - - commonlength) + (*cur_diff).text; - text_insert = text_insert.substr(0, text_insert.length() - - commonlength); - text_delete = text_delete.substr(0, text_delete.length() - - commonlength); - } - } - // Insert the merged records. - if (!text_delete.empty()) { - diffs.insert(cur_diff, Diff(DELETE, text_delete)); - } - if (!text_insert.empty()) { - diffs.insert(cur_diff, Diff(INSERT, text_insert)); - } - } else if (prevEqual != NULL) { - // Merge this equality with the previous one. - prevEqual->text += (*cur_diff).text; - diffs.erase(cur_diff--); - } - - count_insert = 0; - count_delete = 0; - text_delete.clear(); - text_insert.clear(); - prevEqual = &*cur_diff; - break; - } - - } - if (diffs.back().text.empty()) { - diffs.pop_back(); // Remove the dummy entry at the end. - } - - /* - * Second pass: look for single edits surrounded on both sides by equalities - * which can be shifted sideways to eliminate an equality. - * e.g: ABAC -> ABAC - */ - bool changes = false; - // Create a new iterator at the start. - // (As opposed to walking the current one back.) - prev_diff = cur_diff = diffs.begin(); - if (prev_diff != diffs.end() && ++cur_diff != diffs.end()) { - // Intentionally ignore the first and last element (don't need checking). - for (typename Diffs::iterator next_diff = cur_diff; ++next_diff != diffs.end(); prev_diff = cur_diff, cur_diff = next_diff) { - if ((*prev_diff).operation == EQUAL && - (*next_diff).operation == EQUAL) { - // This is a single edit surrounded by equalities. - if ((*cur_diff).text.size() >= (*prev_diff).text.size() && - (*cur_diff).text.compare((*cur_diff).text.size() - (*prev_diff).text.size(), (*prev_diff).text.size(), (*prev_diff).text) == 0) { - // Shift the edit over the previous equality. - (*cur_diff).text = (*prev_diff).text - + (*cur_diff).text.substr(0, (*cur_diff).text.length() - - (*prev_diff).text.length()); - (*next_diff).text = (*prev_diff).text + (*next_diff).text; - diffs.erase(prev_diff); - cur_diff = next_diff; - changes = true; - if (++next_diff == diffs.end()) break; - } else if ((*cur_diff).text.size() >= (*next_diff).text.size() && (*cur_diff).text.compare(0, (*next_diff).text.size(), (*next_diff).text) == 0) { - // Shift the edit over the next equality. - (*prev_diff).text += (*next_diff).text; - (*cur_diff).text = safeMid((*cur_diff).text, (*next_diff).text.length()) - + (*next_diff).text; - next_diff = diffs.erase(next_diff); // Delete nextDiff. - changes = true; - if (next_diff == diffs.end()) break; - } - } - } - } - // If shifts were made, the diff needs reordering and another shift sweep. - if (changes) { - diff_cleanupMerge(diffs); - } - } - - /** - * loc is a location in text1, compute and return the equivalent location in - * text2. - * e.g. "The cat" vs "The big cat", 1->1, 5->8 - * @param diffs LinkedList of Diff objects. - * @param loc Location within text1. - * @return Location within text2. - */ - public: - static int diff_xIndex(const Diffs &diffs, int loc) { - int chars1 = 0; - int chars2 = 0; - int last_chars1 = 0; - int last_chars2 = 0; - typename Diffs::const_iterator last_diff = diffs.end(), cur_diff; - for (cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - if ((*cur_diff).operation != INSERT) { - // Equality or deletion. - chars1 += (*cur_diff).text.length(); - } - if ((*cur_diff).operation != DELETE) { - // Equality or insertion. - chars2 += (*cur_diff).text.length(); - } - if (chars1 > loc) { - // Overshot the location. - last_diff = cur_diff; - break; - } - last_chars1 = chars1; - last_chars2 = chars2; - } - if (last_diff != diffs.end() && (*last_diff).operation == DELETE) { - // The location was deleted. - return last_chars2; - } - // Add the remaining character length. - return last_chars2 + (loc - last_chars1); - } - - /** - * Convert a Diff list into a pretty HTML report. - * @param diffs LinkedList of Diff objects. - * @return HTML representation. - */ - public: - static string_t diff_prettyHtml(const Diffs &diffs) { - string_t html; - string_t text; - for (typename Diffs::const_iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - typename string_t::size_type n = (*cur_diff).text.size(); - typename string_t::const_pointer p, end; - for (p = (*cur_diff).text.c_str(), end = p + n; p != end; ++p) - switch (traits::to_wchar(*p)) { - case L'&': n += 4; break; - case L'<': - case L'>': n += 3; break; - case L'\n': n += 9; break; - } - if (n == (*cur_diff).text.size()) - text = (*cur_diff).text; - else { - text.clear(); - text.reserve(n); - for (p = (*cur_diff).text.c_str(); p != end; ++p) - switch (traits::to_wchar(*p)) { - case L'&': text += traits::cs(L"&"); break; - case L'<': text += traits::cs(L"<"); break; - case L'>': text += traits::cs(L">"); break; - case L'\n': text += traits::cs(L"¶
"); break; - default: text += *p; - } - } - switch ((*cur_diff).operation) { - case INSERT: - html += traits::cs(L"") + text + traits::cs(L""); - break; - case DELETE: - html += traits::cs(L"") + text + traits::cs(L""); - break; - case EQUAL: - html += traits::cs(L"") + text + traits::cs(L""); - break; - } - } - return html; - } - - /** - * Compute and return the source text (all equalities and deletions). - * @param diffs LinkedList of Diff objects. - * @return Source text. - */ - public: - static string_t diff_text1(const Diffs &diffs) { - string_t text; - for (typename Diffs::const_iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - if ((*cur_diff).operation != INSERT) { - text += (*cur_diff).text; - } - } - return text; - } - - /** - * Compute and return the destination text (all equalities and insertions). - * @param diffs LinkedList of Diff objects. - * @return Destination text. - */ - public: - static string_t diff_text2(const Diffs &diffs) { - string_t text; - for (typename Diffs::const_iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - if ((*cur_diff).operation != DELETE) { - text += (*cur_diff).text; - } - } - return text; - } - - /** - * Compute the Levenshtein distance; the number of inserted, deleted or - * substituted characters. - * @param diffs LinkedList of Diff objects. - * @return Number of changes. - */ - public: - static int diff_levenshtein(const Diffs &diffs) { - int levenshtein = 0; - int insertions = 0; - int deletions = 0; - for (typename Diffs::const_iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - switch ((*cur_diff).operation) { - case INSERT: - insertions += (*cur_diff).text.length(); - break; - case DELETE: - deletions += (*cur_diff).text.length(); - break; - case EQUAL: - // A deletion and an insertion is one substitution. - levenshtein += std::max(insertions, deletions); - insertions = 0; - deletions = 0; - break; - } - } - levenshtein += std::max(insertions, deletions); - return levenshtein; - } - - /** - * Crush the diff into an encoded string which describes the operations - * required to transform text1 into text2. - * E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. - * Operations are tab-separated. Inserted text is escaped using %xx notation. - * @param diffs Array of diff tuples. - * @return Delta text. - */ - public: - static string_t diff_toDelta(const Diffs &diffs) { - string_t text; - for (typename Diffs::const_iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - switch ((*cur_diff).operation) { - case INSERT: { - text += traits::from_wchar(L'+'); - append_percent_encoded(text, (*cur_diff).text); - text += traits::from_wchar(L'\t'); - break; - } - case DELETE: - text += traits::from_wchar(L'-') + to_string((*cur_diff).text.length()) + traits::from_wchar(L'\t'); - break; - case EQUAL: - text += traits::from_wchar(L'=') + to_string((*cur_diff).text.length()) + traits::from_wchar(L'\t'); - break; - } - } - if (!text.empty()) { - // Strip off trailing tab character. - text = text.substr(0, text.length() - 1); - } - return text; - } - - /** - * Given the original text1, and an encoded string which describes the - * operations required to transform text1 into text2, compute the full diff. - * @param text1 Source string for the diff. - * @param delta Delta text. - * @return Array of diff tuples or null if invalid. - * @throws string_t If invalid input. - */ - public: - static Diffs diff_fromDelta(const string_t &text1, const string_t &delta) { - Diffs diffs; - int pointer = 0; // Cursor in text1 - typename string_t::size_type token_len; - for (typename string_t::const_pointer token = delta.c_str(); token - delta.c_str() < (int)delta.length(); token += token_len + 1) { - token_len = next_token(delta, traits::from_wchar(L'\t'), token); - if (token_len == 0) { - // Blank tokens are ok (from a trailing \t). - continue; - } - // Each token begins with a one character parameter which specifies the - // operation of this token (delete, insert, equality). - string_t param(token + 1, token_len - 1); - switch (traits::to_wchar(*token)) { - case L'+': - percent_decode(param); - diffs.push_back(Diff(INSERT, param)); - break; - case L'-': - // Fall through. - case L'=': { - int n; - n = to_int(param); - if (n < 0) { - throw string_t(traits::cs(L"Negative number in diff_fromDelta: ") + param); - } - string_t text; - text = safeMid(text1, pointer, n); - pointer += n; - if (traits::to_wchar(*token) == L'=') { - diffs.push_back(Diff(EQUAL, text)); - } else { - diffs.push_back(Diff(DELETE, text)); - } - break; - } - default: - throw string_t(string_t(traits::cs(L"Invalid diff operation in diff_fromDelta: ")) + *token); - } - } - if (pointer != text1.length()) { - throw string_t(traits::cs(L"Delta length (") + to_string(pointer) - + traits::cs(L") smaller than source text length (") - + to_string(text1.length()) + traits::from_wchar(L')')); - } - return diffs; - } - - - // MATCH FUNCTIONS - - - /** - * Locate the best instance of 'pattern' in 'text' near 'loc'. - * Returns -1 if no match found. - * @param text The text to search. - * @param pattern The pattern to search for. - * @param loc The location to search around. - * @return Best match index or -1. - */ - public: - int match_main(const string_t &text, const string_t &pattern, int loc) const { - loc = std::max(0, std::min(loc, (int)text.length())); - if (text == pattern) { - // Shortcut (potentially not guaranteed by the algorithm) - return 0; - } else if (text.empty()) { - // Nothing to match. - return -1; - } else if (loc + pattern.length() <= text.length() - && safeMid(text, loc, pattern.length()) == pattern) { - // Perfect match at the perfect spot! (Includes case of null pattern) - return loc; - } else { - // Do a fuzzy compare. - return match_bitap(text, pattern, loc); - } - } - - /** - * Locate the best instance of 'pattern' in 'text' near 'loc' using the - * Bitap algorithm. Returns -1 if no match found. - * @param text The text to search. - * @param pattern The pattern to search for. - * @param loc The location to search around. - * @return Best match index or -1. - */ - protected: - int match_bitap(const string_t &text, const string_t &pattern, int loc) const { - if (!(Match_MaxBits == 0 || (int)pattern.length() <= Match_MaxBits)) { - throw string_t(traits::cs(L"Pattern too long for this application.")); - } - - // Initialise the alphabet. - std::map s; - match_alphabet(pattern, s); - - // Highest score beyond which we give up. - double score_threshold = Match_Threshold; - // Is there a nearby exact match? (speedup) - size_t best_loc = text.find(pattern, loc); - if (best_loc != string_t::npos) { - score_threshold = std::min(match_bitapScore(0, best_loc, loc, pattern), - score_threshold); - // What about in the other direction? (speedup) - best_loc = text.rfind(pattern, loc + pattern.length()); - if (best_loc != string_t::npos) { - score_threshold = std::min(match_bitapScore(0, best_loc, loc, pattern), - score_threshold); - } - } - - // Initialise the bit arrays. - int matchmask = 1 << (pattern.length() - 1); - best_loc = -1; - - int bin_min, bin_mid; - int bin_max = pattern.length() + text.length(); - int *rd; - int *last_rd = NULL; - for (int d = 0; d < (int)pattern.length(); d++) { - // Scan for the best match; each iteration allows for one more error. - // Run a binary search to determine how far from 'loc' we can stray at - // this error level. - bin_min = 0; - bin_mid = bin_max; - while (bin_min < bin_mid) { - if (match_bitapScore(d, loc + bin_mid, loc, pattern) - <= score_threshold) { - bin_min = bin_mid; - } else { - bin_max = bin_mid; - } - bin_mid = (bin_max - bin_min) / 2 + bin_min; - } - // Use the result from this iteration as the maximum for the next. - bin_max = bin_mid; - int start = std::max(1, loc - bin_mid + 1); - int finish = std::min(loc + bin_mid, (int)text.length()) + pattern.length(); - - rd = new int[finish + 2]; - rd[finish + 1] = (1 << d) - 1; - for (int j = finish; j >= start; j--) { - int charMatch; - if ((int)text.length() <= j - 1) { - // Out of range. - charMatch = 0; - } else { - charMatch = s[text[j - 1]]; - } - if (d == 0) { - // First pass: exact match. - rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; - } else { - // Subsequent passes: fuzzy match. - rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) - | (((last_rd[j + 1] | last_rd[j]) << 1) | 1) - | last_rd[j + 1]; - } - if ((rd[j] & matchmask) != 0) { - double score = match_bitapScore(d, j - 1, loc, pattern); - // This match will almost certainly be better than any existing - // match. But check anyway. - if (score <= score_threshold) { - // Told you so. - score_threshold = score; - best_loc = j - 1; - if (best_loc > loc) { - // When passing loc, don't exceed our current distance from loc. - start = std::max(1, 2 * loc - (int)best_loc); - } else { - // Already passed loc, downhill from here on in. - break; - } - } - } - } - if (match_bitapScore(d + 1, loc, loc, pattern) > score_threshold) { - // No hope for a (better) match at greater error levels. - break; - } - delete [] last_rd; - last_rd = rd; - } - delete [] last_rd; - delete [] rd; - return best_loc; - } - - /** - * Compute and return the score for a match with e errors and x location. - * @param e Number of errors in match. - * @param x Location of match. - * @param loc Expected location of match. - * @param pattern Pattern being sought. - * @return Overall score for match (0.0 = good, 1.0 = bad). - */ - private: - double match_bitapScore(int e, int x, int loc, const string_t &pattern) const { - const float accuracy = static_cast (e) / pattern.length(); - const int proximity = (loc - x < 0)? (x - loc) : (loc - x); - if (Match_Distance == 0) { - // Dodge divide by zero error. - return proximity == 0 ? accuracy : 1.0; - } - return accuracy + (proximity / static_cast (Match_Distance)); - } - - /** - * Initialise the alphabet for the Bitap algorithm. - * @param pattern The text to encode. - * @return Hash of character locations. - */ - protected: - static void match_alphabet(const string_t &pattern, std::map& s) { - // There is no need to initialize map values, since they are zero-initialized by default - for (size_t i = 0; i < pattern.length(); i++) - s[pattern[i]] |= (1 << (pattern.length() - i - 1)); - } - - - // PATCH FUNCTIONS - - - /** - * Increase the context until it is unique, - * but don't let the pattern expand beyond Match_MaxBits. - * @param patch The patch to grow. - * @param text Source text. - */ - protected: - void patch_addContext(Patch &patch, const string_t &text) const { - if (text.empty()) { - return; - } - string_t pattern = safeMid(text, patch.start2, patch.length1); - int padding = 0; - - // Look for the first and last matches of pattern in text. If two different - // matches are found, increase the pattern length. - while (text.find(pattern) != text.rfind(pattern) - && (int)pattern.length() < Match_MaxBits - Patch_Margin - Patch_Margin) { - padding += Patch_Margin; - pattern = safeMid(text, std::max(0, patch.start2 - padding), - std::min((int)text.length(), patch.start2 + patch.length1 + padding) - - std::max(0, patch.start2 - padding)); - } - // Add one chunk for good luck. - padding += Patch_Margin; - - // Add the prefix. - string_t prefix = safeMid(text, std::max(0, patch.start2 - padding), - patch.start2 - std::max(0, patch.start2 - padding)); - if (!prefix.empty()) { - patch.diffs.push_front(Diff(EQUAL, prefix)); - } - // Add the suffix. - string_t suffix = safeMid(text, patch.start2 + patch.length1, - std::min((int)text.length(), patch.start2 + patch.length1 + padding) - - (patch.start2 + patch.length1)); - if (!suffix.empty()) { - patch.diffs.push_back(Diff(EQUAL, suffix)); - } - - // Roll back the start points. - patch.start1 -= prefix.length(); - patch.start2 -= prefix.length(); - // Extend the lengths. - patch.length1 += prefix.length() + suffix.length(); - patch.length2 += prefix.length() + suffix.length(); - } - - /** - * Compute a list of patches to turn text1 into text2. - * A set of diffs will be computed. - * @param text1 Old text. - * @param text2 New text. - * @return LinkedList of Patch objects. - */ - public: - Patches patch_make(const string_t &text1, const string_t &text2) const { - // No diffs provided, compute our own. - Diffs diffs = diff_main(text1, text2, true); - if (diffs.size() > 2) { - diff_cleanupSemantic(diffs); - diff_cleanupEfficiency(diffs); - } - - return patch_make(text1, diffs); - } - - /** - * Compute a list of patches to turn text1 into text2. - * text1 will be derived from the provided diffs. - * @param diffs Array of diff tuples for text1 to text2. - * @return LinkedList of Patch objects. - */ - public: - Patches patch_make(const Diffs &diffs) const { - // No origin string provided, compute our own. - return patch_make(diff_text1(diffs), diffs); - } - - /** - * Compute a list of patches to turn text1 into text2. - * text2 is ignored, diffs are the delta between text1 and text2. - * @param text1 Old text. - * @param text2 Ignored. - * @param diffs Array of diff tuples for text1 to text2. - * @return LinkedList of Patch objects. - * @deprecated Prefer patch_make(const string_t &text1, const Diffs &diffs). - */ - public: - Patches patch_make(const string_t &text1, const string_t &/*text2*/, const Diffs &diffs) const { - return patch_make(text1, diffs); // text2 is entirely unused. - } - - /** - * Compute a list of patches to turn text1 into text2. - * text2 is not provided, diffs are the delta between text1 and text2. - * @param text1 Old text. - * @param diffs Array of diff tuples for text1 to text2. - * @return LinkedList of Patch objects. - */ - public: - Patches patch_make(const string_t &text1, const Diffs &diffs) const { - Patches patches; - if (!diffs.empty()) { // Get rid of the null case. - Patch patch; - int char_count1 = 0; // Number of characters into the text1 string. - int char_count2 = 0; // Number of characters into the text2 string. - // Start with text1 (prepatch_text) and apply the diffs until we arrive at - // text2 (postpatch_text). We recreate the patches one by one to determine - // context info. - string_t prepatch_text = text1; - string_t postpatch_text = text1; - for (typename Diffs::const_iterator cur_diff = diffs.begin(); cur_diff != diffs.end(); ++cur_diff) { - if (patch.diffs.empty() && (*cur_diff).operation != EQUAL) { - // A new patch starts here. - patch.start1 = char_count1; - patch.start2 = char_count2; - } - - switch ((*cur_diff).operation) { - case INSERT: - patch.diffs.push_back(*cur_diff); - patch.length2 += (*cur_diff).text.length(); - postpatch_text = postpatch_text.substr(0, char_count2) - + (*cur_diff).text + safeMid(postpatch_text, char_count2); - break; - case DELETE: - patch.length1 += (*cur_diff).text.length(); - patch.diffs.push_back(*cur_diff); - postpatch_text = postpatch_text.substr(0, char_count2) - + safeMid(postpatch_text, char_count2 + (*cur_diff).text.length()); - break; - case EQUAL: - if ((int)(*cur_diff).text.length() <= 2 * Patch_Margin - && !patch.diffs.empty() && !(*cur_diff == diffs.back())) { - // Small equality inside a patch. - patch.diffs.push_back(*cur_diff); - patch.length1 += (*cur_diff).text.length(); - patch.length2 += (*cur_diff).text.length(); - } - - if ((int)(*cur_diff).text.length() >= 2 * Patch_Margin) { - // Time for a new patch. - if (!patch.diffs.empty()) { - patch_addContext(patch, prepatch_text); - patches.push_back(patch); - patch = Patch(); - // Unlike Unidiff, our patch lists have a rolling context. - // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff - // Update prepatch text & pos to reflect the application of the - // just completed patch. - prepatch_text = postpatch_text; - char_count1 = char_count2; - } - } - break; - } - - // Update the current character count. - if ((*cur_diff).operation != INSERT) { - char_count1 += (*cur_diff).text.length(); - } - if ((*cur_diff).operation != DELETE) { - char_count2 += (*cur_diff).text.length(); - } - } - // Pick up the leftover patch if not empty. - if (!patch.diffs.empty()) { - patch_addContext(patch, prepatch_text); - patches.push_back(patch); - } - } - return patches; - } - - /** - * Given an array of patches, return another array that is identical. - * @param patches Array of patch objects. - * @return Array of patch objects. - */ - public: - Patches patch_deepCopy(const Patches &patches) const { return patches; } - - /** - * Merge a set of patches onto the text. Return a patched text, as well - * as an array of true/false values indicating which patches were applied. - * @param patches Array of patch objects. - * @param text Old text. - * @return Two element Object array, containing the new text and an array of - * boolean values. - */ - public: - std::pair > patch_apply(const Patches &patches, const string_t &text) const - { std::pair > res; patch_apply(patches, text, res); return res; } - void patch_apply(const Patches &patches, const string_t &sourceText, std::pair >& res) const { - if (patches.empty()) { - res.first = sourceText; - res.second.clear(); - return; - } - string_t text = sourceText; // Copy to preserve original. - - // Deep copy the patches so that no changes are made to originals. - // Patches patchesCopy = patch_deepCopy(patches); - Patches patchesCopy(patches); // Default copy constructor will do it just fine - - string_t nullPadding = patch_addPadding(patchesCopy); - text = nullPadding + text + nullPadding; - patch_splitMax(patchesCopy); - - int x = 0; - // delta keeps track of the offset between the expected and actual location - // of the previous patch. If there are patches expected at positions 10 and - // 20, but the first patch was found at 12, delta is 2 and the second patch - // has an effective expected position of 22. - int delta = 0; - std::vector& results = res.second; - results.resize(patchesCopy.size()); - string_t text1, text2; - for (typename Patches::const_iterator cur_patch = patchesCopy.begin(); cur_patch != patchesCopy.end(); ++cur_patch) { - int expected_loc = (*cur_patch).start2 + delta; - text1 = diff_text1((*cur_patch).diffs); - int start_loc; - int end_loc = -1; - if ((int)text1.length() > Match_MaxBits) { - // patch_splitMax will only provide an oversized pattern in the case of - // a monster delete. - start_loc = match_main(text, text1.substr(0, Match_MaxBits), expected_loc); - if (start_loc != -1) { - end_loc = match_main(text, right(text1, Match_MaxBits), - expected_loc + text1.length() - Match_MaxBits); - if (end_loc == -1 || start_loc >= end_loc) { - // Can't find valid trailing context. Drop this patch. - start_loc = -1; - } - } - } else { - start_loc = match_main(text, text1, expected_loc); - } - if (start_loc == -1) { - // No match found. :( - results[x] = false; - // Subtract the delta for this failed patch from subsequent patches. - delta -= (*cur_patch).length2 - (*cur_patch).length1; - } else { - // Found a match. :) - results[x] = true; - delta = start_loc - expected_loc; - if (end_loc == -1) { - text2 = safeMid(text, start_loc, text1.length()); - } else { - text2 = safeMid(text, start_loc, end_loc + Match_MaxBits - start_loc); - } - if (text1 == text2) { - // Perfect match, just shove the replacement text in. - text = text.substr(0, start_loc) + diff_text2((*cur_patch).diffs) + safeMid(text, start_loc + text1.length()); - } else { - // Imperfect match. Run a diff to get a framework of equivalent - // indices. - Diffs diffs = diff_main(text1, text2, false); - if ((int)text1.length() > Match_MaxBits - && diff_levenshtein(diffs) / static_cast (text1.length()) - > Patch_DeleteThreshold) { - // The end points match, but the content is unacceptably bad. - results[x] = false; - } else { - diff_cleanupSemanticLossless(diffs); - int index1 = 0; - for (typename Diffs::const_iterator cur_diff = (*cur_patch).diffs.begin(); cur_diff != (*cur_patch).diffs.end(); ++cur_diff) { - if ((*cur_diff).operation != EQUAL) { - int index2 = diff_xIndex(diffs, index1); - if ((*cur_diff).operation == INSERT) { - // Insertion - text = text.substr(0, start_loc + index2) + (*cur_diff).text - + safeMid(text, start_loc + index2); - } else if ((*cur_diff).operation == DELETE) { - // Deletion - text = text.substr(0, start_loc + index2) - + safeMid(text, start_loc + diff_xIndex(diffs, - index1 + (*cur_diff).text.length())); - } - } - if ((*cur_diff).operation != DELETE) { - index1 += (*cur_diff).text.length(); - } - } - } - } - } - x++; - } - // Strip the padding off. - res.first = safeMid(text, nullPadding.length(), text.length() - 2 * nullPadding.length()); - } - - /** - * Add some padding on text start and end so that edges can match something. - * Intended to be called only from within patch_apply. - * @param patches Array of patch objects. - * @return The padding string added to each side. - */ - public: - string_t patch_addPadding(Patches &patches) const { - short paddingLength = Patch_Margin; - string_t nullPadding; - for (short x = 1; x <= paddingLength; x++) { - nullPadding += (char_t)x; - } - - // Bump all the patches forward. - for (typename Patches::iterator cur_patch = patches.begin(); cur_patch != patches.end(); ++cur_patch) { - (*cur_patch).start1 += paddingLength; - (*cur_patch).start2 += paddingLength; - } - - // Add some padding on start of first diff. - Patch &firstPatch = patches.front(); - Diffs &firstPatchDiffs = firstPatch.diffs; - if (firstPatchDiffs.empty() || firstPatchDiffs.front().operation != EQUAL) { - // Add nullPadding equality. - firstPatchDiffs.push_front(Diff(EQUAL, nullPadding)); - firstPatch.start1 -= paddingLength; // Should be 0. - firstPatch.start2 -= paddingLength; // Should be 0. - firstPatch.length1 += paddingLength; - firstPatch.length2 += paddingLength; - } else if (paddingLength > (int)firstPatchDiffs.front().text.length()) { - // Grow first equality. - Diff &firstDiff = firstPatchDiffs.front(); - int extraLength = paddingLength - firstDiff.text.length(); - firstDiff.text = safeMid(nullPadding, firstDiff.text.length(), - paddingLength - firstDiff.text.length()) + firstDiff.text; - firstPatch.start1 -= extraLength; - firstPatch.start2 -= extraLength; - firstPatch.length1 += extraLength; - firstPatch.length2 += extraLength; - } - - // Add some padding on end of last diff. - Patch &lastPatch = patches.front(); - Diffs &lastPatchDiffs = lastPatch.diffs; - if (lastPatchDiffs.empty() || lastPatchDiffs.back().operation != EQUAL) { - // Add nullPadding equality. - lastPatchDiffs.push_back(Diff(EQUAL, nullPadding)); - lastPatch.length1 += paddingLength; - lastPatch.length2 += paddingLength; - } else if (paddingLength > (int)lastPatchDiffs.back().text.length()) { - // Grow last equality. - Diff &lastDiff = lastPatchDiffs.back(); - int extraLength = paddingLength - lastDiff.text.length(); - lastDiff.text += nullPadding.substr(0, extraLength); - lastPatch.length1 += extraLength; - lastPatch.length2 += extraLength; - } - - return nullPadding; - } - - /** - * Look through the patches and break up any which are longer than the - * maximum limit of the match algorithm. - * Intended to be called only from within patch_apply. - * @param patches LinkedList of Patch objects. - */ - public: - void patch_splitMax(Patches &patches) const { - short patch_size = Match_MaxBits; - string_t precontext, postcontext; - Patch patch; - int start1, start2; - bool empty; - Operation diff_type; - string_t diff_text; - Patch bigpatch; - - for (typename Patches::iterator cur_patch = patches.begin(); cur_patch != patches.end();) { - if ((*cur_patch).length1 <= patch_size) { ++cur_patch; continue; } - bigpatch = *cur_patch; - // Remove the big old patch. - cur_patch = patches.erase(cur_patch); - start1 = bigpatch.start1; - start2 = bigpatch.start2; - precontext.clear(); - while (!bigpatch.diffs.empty()) { - // Create one of several smaller patches. - patch = Patch(); - empty = true; - patch.start1 = start1 - precontext.length(); - patch.start2 = start2 - precontext.length(); - if (!precontext.empty()) { - patch.length1 = patch.length2 = precontext.length(); - patch.diffs.push_back(Diff(EQUAL, precontext)); - } - while (!bigpatch.diffs.empty() - && patch.length1 < patch_size - Patch_Margin) { - diff_type = bigpatch.diffs.front().operation; - diff_text = bigpatch.diffs.front().text; - if (diff_type == INSERT) { - // Insertions are harmless. - patch.length2 += diff_text.length(); - start2 += diff_text.length(); - patch.diffs.push_back(bigpatch.diffs.front()); - bigpatch.diffs.pop_front(); - empty = false; - } else if (diff_type == DELETE && patch.diffs.size() == 1 - && patch.diffs.front().operation == EQUAL - && (int)diff_text.length() > 2 * patch_size) { - // This is a large deletion. Let it pass in one chunk. - patch.length1 += diff_text.length(); - start1 += diff_text.length(); - empty = false; - patch.diffs.push_back(Diff(diff_type, diff_text)); - bigpatch.diffs.pop_front(); - } else { - // Deletion or equality. Only take as much as we can stomach. - diff_text = diff_text.substr(0, std::min((int)diff_text.length(), - patch_size - patch.length1 - Patch_Margin)); - patch.length1 += diff_text.length(); - start1 += diff_text.length(); - if (diff_type == EQUAL) { - patch.length2 += diff_text.length(); - start2 += diff_text.length(); - } else { - empty = false; - } - patch.diffs.push_back(Diff(diff_type, diff_text)); - if (diff_text == bigpatch.diffs.front().text) { - bigpatch.diffs.pop_front(); - } else { - bigpatch.diffs.front().text = safeMid(bigpatch.diffs.front().text, diff_text.length()); - } - } - } - // Compute the head context for the next patch. - precontext = safeMid(diff_text2(patch.diffs), std::max(0, (int)precontext.length() - Patch_Margin)); - // Append the end context for this patch. - postcontext = diff_text1(bigpatch.diffs); - if ((int)postcontext.length() > Patch_Margin) { - postcontext = postcontext.substr(0, Patch_Margin); - } - if (!postcontext.empty()) { - patch.length1 += postcontext.length(); - patch.length2 += postcontext.length(); - if (!patch.diffs.empty() - && patch.diffs.back().operation == EQUAL) { - patch.diffs.back().text += postcontext; - } else { - patch.diffs.push_back(Diff(EQUAL, postcontext)); - } - } - if (!empty) { - patches.insert(cur_patch, patch); - } - } - } - } - - /** - * Take a list of patches and return a textual representation. - * @param patches List of Patch objects. - * @return Text representation of patches. - */ - public: - static string_t patch_toText(const Patches &patches) { - string_t text; - for (typename Patches::const_iterator cur_patch = patches.begin(); cur_patch != patches.end(); ++cur_patch) { - text += (*cur_patch).toString(); - } - return text; - } - - /** - * Parse a textual representation of patches and return a List of Patch - * objects. - * @param textline Text representation of patches. - * @return List of Patch objects. - * @throws string_t If invalid input. - */ - public: - Patches patch_fromText(const string_t &textline) const { - Patches patches; - if (!textline.empty()) { - char_t sign; - string_t line; - typename string_t::const_pointer text = textline.c_str(); - typename string_t::size_type text_len, l; - while (text - textline.c_str() < (int)textline.length()) { - if ((text_len = next_token(textline, traits::from_wchar(L'\n'), text)) == 0) { ++text; continue; } - - // A replacement for the regexp "^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$" exact match - string_t start1, length1, start2, length2; - do { - typename string_t::const_pointer t = text; - l = text_len; - if ((l -= 9) > 0 && traits::to_wchar(*t) == L'@' && traits::to_wchar(*++t) == L'@' - && traits::to_wchar(*++t) == L' ' && traits::to_wchar(*++t) == L'-' && traits::is_digit(*++t)) { - do { start1 += *t; } while (--l > 0 && traits::is_digit(*++t)); - if (l > 0 && traits::to_wchar(*t) == L',') ++t, --l; - while (l > 0 && traits::is_digit(*t)) --l, length1 += *t++; - if (l > 0 && traits::to_wchar(*t++) == L' ' && traits::to_wchar(*t++) == L'+' && traits::is_digit(*t)) { - do { start2 += *t; --l; } while (traits::is_digit(*++t)); - if (l > 0 && traits::to_wchar(*t) == L',') ++t, --l; - while (l > 0 && traits::is_digit(*t)) --l, length2 += *t++; - if (l == 0 && traits::to_wchar(*t++) == L' ' && traits::to_wchar(*t++) == L'@' && traits::to_wchar(*t) == L'@') break; // Success - } - } - throw string_t(traits::cs(L"Invalid patch string: ") + string_t(text, text_len)); - } while (false); - - Patch patch; - patch.start1 = to_int(start1); - if (length1.empty()) { - patch.start1--; - patch.length1 = 1; - } else if (length1.size() == 1 && traits::to_wchar(length1[0]) == L'0') { - patch.length1 = 0; - } else { - patch.start1--; - patch.length1 = to_int(length1); - } - - patch.start2 = to_int(start2); - if (length2.empty()) { - patch.start2--; - patch.length2 = 1; - } else if (length2.size() == 1 && traits::to_wchar(length2[0]) == L'0') { - patch.length2 = 0; - } else { - patch.start2--; - patch.length2 = to_int(length2); - } - - for (text += text_len + 1; text - textline.c_str() < (int)textline.length(); text += text_len + 1) { - if ((text_len = next_token(textline, traits::from_wchar(L'\n'), text)) == 0) continue; - - sign = *text; - line.assign(text + 1, text_len - 1); - percent_decode(line); - switch (traits::to_wchar(sign)) { - case L'-': - // Deletion. - patch.diffs.push_back(Diff(DELETE, line)); - continue; - case L'+': - // Insertion. - patch.diffs.push_back(Diff(INSERT, line)); - continue; - case L' ': - // Minor equality. - patch.diffs.push_back(Diff(EQUAL, line)); - continue; - case L'@': - // Start of next patch. - break; - default: - // WTF? - throw string_t(traits::cs(L"Invalid patch mode '") + (sign + (traits::cs(L"' in: ") + line))); - } - break; - } - - patches.push_back(patch); - } - } - return patches; - } - - /** - * A safer version of string_t.mid(pos). This one returns "" instead of - * null when the position equals the string length. - * @param str String to take a substring from. - * @param pos Position to start the substring from. - * @return Substring. - */ - private: - static inline string_t safeMid(const string_t &str, size_t pos) { - return (pos == str.length()) ? string_t() : str.substr(pos); - } - - /** - * A safer version of string_t.mid(pos, len). This one returns "" instead of - * null when the position equals the string length. - * @param str String to take a substring from. - * @param pos Position to start the substring from. - * @param len Length of substring. - * @return Substring. - */ - private: - static inline string_t safeMid(const string_t &str, size_t pos, size_t len) { - return (pos == str.length()) ? string_t() : str.substr(pos, len); - } - - /** - * Utility functions - */ - private: - static string_t to_string(int n) { - string_t str; - bool negative = false; - size_t l = 0; - if (n < 0) n = -n, ++l, negative = true; - int n_ = n; do { ++l; } while ((n_ /= 10) > 0); - str.resize(l); - typename string_t::iterator s = str.end(); - const wchar_t digits[] = L"0123456789"; - do { *--s = traits::from_wchar(digits[n % 10]); } while ((n /= 10) > 0); - if (negative) *--s = traits::from_wchar(L'-'); - return str; - } - - static int to_int(const string_t& str) { return traits::to_int(str.c_str()); } - - static bool is_control(char_t c) { switch (traits::to_wchar(c)) { case L'\n': case L'\r': return true; } return false; } - - static typename string_t::size_type next_token(const string_t& str, char_t delim, typename string_t::const_pointer off) { - typename string_t::const_pointer p = off, end = str.c_str() + str.length(); - for (; p != end; ++p) if (*p == delim) break; - return p - off; - } - - static void append_percent_encoded(string_t& s1, const string_t& s2) { - const wchar_t safe_chars[] = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~ !*'();/?:@&=+$,#"; - - size_t safe[0x100], i; - for (i = 0; i < 0x100; ++i) safe[i] = 0; - for (i = 0; i < sizeof(safe_chars) / sizeof(wchar_t); ++i) safe[safe_chars[i]] = i + 1; - - int n = 0; - typename traits::utf32_t u; - typename string_t::const_pointer c = s2.c_str(), end = c + s2.length(); - while (c != end) { - c = traits::to_utf32(c, end, u); - n += u >= 0x10000? 12 : u >= 0x800? 9 : u >= 0x80? 6 : safe[static_cast(u)]? 1 : 3; - } - if (n == int(s2.length())) - s1.append(s2); - else { - s1.reserve(s1.size() + n); - // Encode as UTF-8, then escape unsafe characters - unsigned char utf8[4]; - for (c = s2.c_str(); c != end;) { - c = traits::to_utf32(c, end, u); - unsigned char* pt = utf8; - if (u < 0x80) - *pt++ = (unsigned char)u; - else if (u < 0x800) { - *pt++ = (unsigned char)((u >> 6) | 0xC0); - *pt++ = (unsigned char)((u & 0x3F) | 0x80); - } - else if (u < 0x10000) { - *pt++ = (unsigned char)((u >> 12) | 0xE0); - *pt++ = (unsigned char)(((u >> 6) & 0x3F) | 0x80); - *pt++ = (unsigned char)((u & 0x3F) | 0x80); - } - else { - *pt++ = (unsigned char)((u >> 18) | 0xF0); - *pt++ = (unsigned char)(((u >> 12) & 0x3F) | 0x80); - *pt++ = (unsigned char)(((u >> 6) & 0x3F) | 0x80); - *pt++ = (unsigned char)((u & 0x3F) | 0x80); - } - - for (const unsigned char* p = utf8; p < pt; ++p) - if (safe[*p]) - s1 += traits::from_wchar(safe_chars[safe[*p] - 1]); - else { - s1 += traits::from_wchar(L'%'); - s1 += traits::from_wchar(safe_chars[(*p & 0xF0) >> 4]); - s1 += traits::from_wchar(safe_chars[*p & 0xF]); - } - } - } - } - - static unsigned hex_digit_value(char_t c) { - switch (traits::to_wchar(c)) - { - case L'0': return 0; - case L'1': return 1; - case L'2': return 2; - case L'3': return 3; - case L'4': return 4; - case L'5': return 5; - case L'6': return 6; - case L'7': return 7; - case L'8': return 8; - case L'9': return 9; - case L'A': case L'a': return 0xA; - case L'B': case L'b': return 0xB; - case L'C': case L'c': return 0xC; - case L'D': case L'd': return 0xD; - case L'E': case L'e': return 0xE; - case L'F': case L'f': return 0xF; - } - throw string_t(string_t(traits::cs(L"Invalid character: ")) + c); - } - - static void percent_decode(string_t& str) { - typename string_t::iterator s2 = str.begin(), s3 = s2, s4 = s2; - for (typename string_t::const_pointer s1 = str.c_str(), end = s1 + str.size(); s1 != end; ++s1, ++s2) - if (traits::to_wchar(*s1) != L'%') - *s2 = *s1; - else { - char_t d1 = *++s1; - *s2 = char_t((hex_digit_value(d1) << 4) + hex_digit_value(*++s1)); - } - // Decode UTF-8 string in-place - while (s3 != s2) { - unsigned u = *s3; - if (u < 0x80) - ; - else if ((u >> 5) == 6) { - if (++s3 == s2 || (*s3 & 0xC0) != 0x80) continue; - u = ((u & 0x1F) << 6) + (*s3 & 0x3F); - } - else if ((u >> 4) == 0xE) { - if (++s3 == s2 || (*s3 & 0xC0) != 0x80) continue; - u = ((u & 0xF) << 12) + ((*s3 & 0x3F) << 6); - if (++s3 == s2 || (*s3 & 0xC0) != 0x80) continue; - u += *s3 & 0x3F; - } - else if ((u >> 3) == 0x1E) { - if (++s3 == s2 || (*s3 & 0xC0) != 0x80) continue; - u = ((u & 7) << 18) + ((*s3 & 0x3F) << 12); - if (++s3 == s2 || (*s3 & 0xC0) != 0x80) continue; - u += (*s3 & 0x3F) << 6; - if (++s3 == s2 || (*s3 & 0xC0) != 0x80) continue; - u += *s3 & 0x3F; - } - else { - ++s3; - continue; - } - s4 = traits::from_utf32(u, s4); - ++s3; - } - if (s4 != str.end()) str.resize(s4 - str.begin()); - } - - static string_t right(const string_t& str, typename string_t::size_type n) { return str.substr(str.size() - n); } -}; - - -/** - * Functions dependent on character type - */ - -// Unicode helpers -template -struct diff_match_patch_utf32_direct { - typedef utf32_type utf32_t; - template static iterator to_utf32(iterator i, iterator /*end*/, utf32_t& u) - { - u = *i++; - return i; - } - template static iterator from_utf32(utf32_t u, iterator o) - { - *o++ = static_cast(u); - return o; - } -}; - -template -struct diff_match_patch_utf32_from_utf16 { - typedef utf32_type utf32_t; - static const unsigned UTF16_SURROGATE_MIN = 0xD800u; - static const unsigned UTF16_SURROGATE_MAX = 0xDFFFu; - static const unsigned UTF16_HIGH_SURROGATE_MAX = 0xDBFFu; - static const unsigned UTF16_LOW_SURROGATE_MIN = 0xDC00u; - static const unsigned UTF16_SURROGATE_OFFSET = (UTF16_SURROGATE_MIN << 10) + UTF16_HIGH_SURROGATE_MAX - 0xFFFFu; - template static iterator to_utf32(iterator i, iterator end, utf32_t& u) - { - u = *i++; - if (UTF16_SURROGATE_MIN <= u && u <= UTF16_HIGH_SURROGATE_MAX && i != end) - u = (u << 10) + *i++ - UTF16_SURROGATE_OFFSET; // Assume it is a UTF-16 surrogate pair - return i; - } - template static iterator from_utf32(utf32_t u, iterator o) - { - if (u > 0xFFFF) { // Encode code points that do not fit in char_t as UTF-16 surrogate pairs - *o++ = static_cast((u >> 10) + UTF16_SURROGATE_MIN - (0x10000 >> 10)); - *o++ = static_cast((u & 0x3FF) + UTF16_LOW_SURROGATE_MIN); - } - else - *o++ = static_cast(u); - return o; - } -}; - -// Specialization of the traits for wchar_t -#include -template <> struct diff_match_patch_traits : diff_match_patch_utf32_from_utf16 { - static bool is_alnum(wchar_t c) { return std::iswalnum(c)? true : false; } - static bool is_digit(wchar_t c) { return std::iswdigit(c)? true : false; } - static bool is_space(wchar_t c) { return std::iswspace(c)? true : false; } - static int to_int(const wchar_t* s) { return static_cast(std::wcstol(s, NULL, 10)); } - static wchar_t from_wchar(wchar_t c) { return c; } - static wchar_t to_wchar(wchar_t c) { return c; } - static const wchar_t* cs(const wchar_t* s) { return s; } - static const wchar_t eol = L'\n'; - static const wchar_t tab = L'\t'; -}; - -// Possible specialization of the traits for char -#include -template <> struct diff_match_patch_traits : diff_match_patch_utf32_direct -{ - static bool is_alnum(char c) { return std::isalnum(c)? true : false; } - static bool is_digit(char c) { return std::isdigit(c)? true : false; } - static bool is_space(char c) { return std::isspace(c)? true : false; } - static int to_int(const char* s) { return std::atoi(s); } - static char from_wchar(wchar_t c) { return static_cast(c); } - static wchar_t to_wchar(char c) { return static_cast(c); } - static std::string cs(const wchar_t* s) { return std::string(s, s + wcslen(s)); } - static const char eol = '\n'; - static const char tab = '\t'; -}; - - -#endif // DIFF_MATCH_PATCH_H diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch_test_string.cpp b/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch_test_string.cpp deleted file mode 100644 index 2d3b1fea96..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch_test_string.cpp +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * Copyright 2008 Google Inc. All Rights Reserved. - * Author: fraser@google.com (Neil Fraser) - * Author: mikeslemmer@gmail.com (Mike Slemmer) - * Author: snhere@gmail.com (Sergey Nozhenko) - * Author: leutloff@sundancer.oche.de (Christian Leutloff) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Diff Match and Patch -- Test Harness using std::string - * http://code.google.com/p/google-diff-match-patch/ - */ - -#include "diff_match_patch.h" -#include -#include -#include -#include - -using namespace std; - -struct wastring : wstring // The same as wstring, but can be constructed from char* (to use ASCII strings in the test functions) -{ - wastring() {} - wastring(const wstring& s) : wstring(s) {} - wastring(const value_type* s) : wstring(s) {} - wastring(const value_type* s, size_t n) : wstring(s, n) {} - wastring(const char* s) : wstring(s, s + strlen(s)) {} - wastring(const char* s, size_t n) : wstring(s, s + n) {} - wastring operator+=(value_type c) { append(1, c); return *this; } - wastring operator+=(const wastring& s) { append(s); return *this; } - wastring operator+=(const char* s) { append(s, s + strlen(s)); return *this; } - - static const wchar_t eol = L'\n'; - static const wchar_t tab = L'\t'; -}; - -inline wastring operator+(const wastring& s, const char* p) { return s + wastring(p); } -inline wastring operator+(const char* p, const wastring& s) { return wastring(p) + s; } - -ostream& operator<<(ostream& o, const wastring& s) -{ - o << setfill('0'); - for (wastring::const_pointer p = s.c_str(), end = p + s.length(); p != end; ++p) - { - if (*p >= 0x80) - o << "\\u" << hex << setw(4) << unsigned(*p); - else if (*p >= ' ') - o << char(*p); - else - switch (char(*p)) { - case '\n': o << "\\n"; break; - case '\r': o << "\\r"; break; - case '\t': o << "\\t"; break; - default: o << "\\x" << hex << setw(2) << unsigned(*p); - } - } - return o; -} - - -#define dmp (*this) - -class diff_match_patch_test : diff_match_patch { - typedef vector Strings; - typedef diff_match_patch_traits traits; - public: - diff_match_patch_test() {} - - void run_all_tests() { - clock_t t = clock(); - try { - testDiffCommonPrefix(); - testDiffCommonSuffix(); - testDiffCommonOverlap(); - testDiffHalfmatch(); - testDiffLinesToChars(); - //TODO testDiffCharsToLines(); - testDiffCleanupMerge(); - testDiffCleanupSemanticLossless(); - testDiffCleanupSemantic(); - testDiffCleanupEfficiency(); - testDiffPrettyHtml(); - testDiffText(); - testDiffDelta(); - testDiffXIndex(); - testDiffLevenshtein(); - testDiffBisect(); - testDiffMain(); - - testMatchAlphabet(); - testMatchBitap(); - testMatchMain(); - - testPatchObj(); - testPatchFromText(); - testPatchToText(); - testPatchAddContext(); - testPatchMake(); - testPatchSplitMax(); - testPatchAddPadding(); - testPatchApply(); - - cout << "All tests passed." << endl; - } catch (const char* strCase) { - cerr << "Test failed: " << strCase << endl; - } - cout << "Total time: " << int((clock() - t) * 1000 / CLOCKS_PER_SEC) << " ms" << endl; - } - - - // DIFF TEST FUNCTIONS - - void testDiffCommonPrefix() { - // Detect any common prefix. - assertEquals("diff_commonPrefix: Null case.", 0, dmp.diff_commonPrefix("abc", "xyz")); - - assertEquals("diff_commonPrefix: Non-null case.", 4, dmp.diff_commonPrefix("1234abcdef", "1234xyz")); - - assertEquals("diff_commonPrefix: Whole case.", 4, dmp.diff_commonPrefix("1234", "1234xyz")); - } - - void testDiffCommonSuffix() { - // Detect any common suffix. - assertEquals("diff_commonSuffix: Null case.", 0, dmp.diff_commonSuffix("abc", "xyz")); - - assertEquals("diff_commonSuffix: Non-null case.", 4, dmp.diff_commonSuffix("abcdef1234", "xyz1234")); - - assertEquals("diff_commonSuffix: Whole case.", 4, dmp.diff_commonSuffix("1234", "xyz1234")); - } - - void testDiffCommonOverlap() { - // Detect any suffix/prefix overlap. - assertEquals("diff_commonOverlap: Null case.", 0, dmp.diff_commonOverlap("", "abcd")); - - assertEquals("diff_commonOverlap: Whole case.", 3, dmp.diff_commonOverlap("abc", "abcd")); - - assertEquals("diff_commonOverlap: No overlap.", 0, dmp.diff_commonOverlap("123456", "abcd")); - - assertEquals("diff_commonOverlap: Overlap.", 3, dmp.diff_commonOverlap("123456xxx", "xxxabcd")); - - // Some overly clever languages (C#) may treat ligatures as equal to their - // component letters. E.g. U+FB01 == 'fi' - assertEquals("diff_commonOverlap: Unicode.", 0, dmp.diff_commonOverlap("fi", "\xEF\xAC\x81i")); -} - - void testDiffHalfmatch() { - // Detect a halfmatch. - dmp.Diff_Timeout = 1; - assertEmpty("diff_halfMatch: No match #1.", diff_halfMatch("1234567890", "abcdef")); - - assertEmpty("diff_halfMatch: No match #2.", diff_halfMatch("12345", "23")); - - assertEquals("diff_halfMatch: Single Match #1.", split("12,90,a,z,345678", ','), diff_halfMatch("1234567890", "a345678z")); - - assertEquals("diff_halfMatch: Single Match #2.", split("a,z,12,90,345678", ','), diff_halfMatch("a345678z", "1234567890")); - - assertEquals("diff_halfMatch: Single Match #3.", split("abc,z,1234,0,56789", ','), diff_halfMatch("abc56789z", "1234567890")); - - assertEquals("diff_halfMatch: Single Match #4.", split("a,xyz,1,7890,23456", ','), diff_halfMatch("a23456xyz", "1234567890")); - - assertEquals("diff_halfMatch: Multiple Matches #1.", split("12123,123121,a,z,1234123451234", ','), diff_halfMatch("121231234123451234123121", "a1234123451234z")); - - assertEquals("diff_halfMatch: Multiple Matches #2.", split(",-=-=-=-=-=,x,,x-=-=-=-=-=-=-=", ','), diff_halfMatch("x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-=")); - - assertEquals("diff_halfMatch: Multiple Matches #3.", split("-=-=-=-=-=,,,y,-=-=-=-=-=-=-=y", ','), diff_halfMatch("-=-=-=-=-=-=-=-=-=-=-=-=y", "-=-=-=-=-=-=-=yy")); - - // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy - assertEquals("diff_halfMatch: Non-optimal halfmatch.", split("qHillo,w,x,Hulloy,HelloHe", ','), diff_halfMatch("qHilloHelloHew", "xHelloHeHulloy")); - - // dmp.Diff_Timeout = 0; - // assertEmpty("diff_halfMatch: Optimal no halfmatch.", diff_halfMatch("qHilloHelloHew", "xHelloHeHulloy")); - } - - void testDiffLinesToChars() { - // Convert lines down to characters. - Lines tmpVector, resVector; - tmpVector.text1 = string_t("alpha\n"); - tmpVector.text2 = string_t("beta\n"); - tmpVector.resize(3); - tmpVector[1] = LinePtr(tmpVector.text1.c_str(), tmpVector.text1.length()); - tmpVector[2] = LinePtr(tmpVector.text2.c_str(), tmpVector.text2.length()); - string_t text1("alpha\nbeta\nalpha\n"), text2("beta\nalpha\nbeta\n"); - dmp.diff_linesToChars(text1, text2, resVector); - assertEquals("diff_linesToChars:", "\1\2\1", "\2\1\2", tmpVector, text1, text2, resVector); - - tmpVector.text1 = string_t("alpha\r\n"); - tmpVector.text2 = string_t("beta\r\n\r\n"); - tmpVector.resize(4); - tmpVector[1] = LinePtr(tmpVector.text1.c_str(), tmpVector.text1.length()); - tmpVector[2] = LinePtr(tmpVector.text2.c_str(), tmpVector.text2.length() - 2); - tmpVector[3] = LinePtr(tmpVector.text2.c_str() + 6, 2); - text1.clear(), text2 = "alpha\r\nbeta\r\n\r\n\r\n"; - dmp.diff_linesToChars(text1, text2, resVector); - assertEquals("diff_linesToChars:", string_t(), "\1\2\3\3", tmpVector, text1, text2, resVector); - - tmpVector.text1 = string_t("a"); - tmpVector.text2 = string_t("b"); - tmpVector.resize(3); - tmpVector[1] = LinePtr(tmpVector.text1.c_str(), tmpVector.text1.length()); - tmpVector[2] = LinePtr(tmpVector.text2.c_str(), tmpVector.text2.length()); - text1 = "a", text2 = "b"; - resVector.clear(); - dmp.diff_linesToChars(text1, text2, resVector); - assertEquals("diff_linesToChars:", "\1", "\2", tmpVector, text1, text2, resVector); - - // More than 256 to reveal any 8-bit limitations. - size_t n = 300; - tmpVector.resize(n + 1); - tmpVector[0].second = 0; - basic_stringstream lines; - string_t chars; - for (size_t x = 1; x < n + 1; x++) { - lines << x << traits::eol; - tmpVector[x].second = lines.str().size(); - chars += (wchar_t)x; - } - tmpVector.text1 = lines.str(); - for (size_t x = 1, prev = 0; x < n + 1; x++) { - tmpVector[x].first = tmpVector.text1.c_str() + prev; - tmpVector[x].second -= prev; - prev += tmpVector[x].second; - } - assertEquals("diff_linesToChars: More than 256 (setup).", n + 1, tmpVector.size()); - assertEquals("diff_linesToChars: More than 256 (setup).", n, chars.length()); - text1 = tmpVector.text1, text2.clear(); - resVector.clear(); - dmp.diff_linesToChars(text1, text2, resVector); - assertEquals("diff_linesToChars:", chars, "", tmpVector, text1, text2, resVector); - } - - void testDiffCharsToLines() { - // First check that Diff equality works. - assertTrue("diff_charsToLines:", Diff(EQUAL, "a") == Diff(EQUAL, "a")); - - assertEquals("diff_charsToLines:", Diff(EQUAL, "a"), Diff(EQUAL, "a")); - - // Convert chars up to lines. - Diffs diffs; - diffs.push_back(Diff(EQUAL, "\1\2\1")); // ("\u0001\u0002\u0001"); - diffs.push_back(Diff(INSERT, "\2\1\2")); // ("\u0002\u0001\u0002"); - Lines tmpVector; - tmpVector.text1 = string_t("alpha\n"); - tmpVector.text2 = string_t("beta\n"); - tmpVector.resize(3); - tmpVector[1] = LinePtr(tmpVector.text1.c_str(), tmpVector.text1.length()); - tmpVector[2] = LinePtr(tmpVector.text2.c_str(), tmpVector.text2.length()); - dmp.diff_charsToLines(diffs, tmpVector); - assertEquals("diff_charsToLines:", diffList(Diff(EQUAL, "alpha\nbeta\nalpha\n"), Diff(INSERT, "beta\nalpha\nbeta\n")), diffs); - - // More than 256 to reveal any 8-bit limitations. - size_t n = 300; - tmpVector.resize(n + 1); - tmpVector[0].second = 0; - basic_stringstream lines; - string_t chars; - for (size_t x = 1; x < n + 1; x++) { - lines << x << traits::eol; - tmpVector[x].second = lines.str().size(); - chars += (char_t)x; - } - tmpVector.text1 = lines.str(); - for (size_t x = 1, prev = 0; x < n + 1; x++) { - tmpVector[x].first = tmpVector.text1.c_str() + prev; - tmpVector[x].second -= prev; - prev += tmpVector[x].second; - } - assertEquals("diff_linesToChars: More than 256 (setup).", n + 1, tmpVector.size()); - assertEquals("diff_linesToChars: More than 256 (setup).", n, chars.length()); - diffs = diffList(Diff(DELETE, chars)); - dmp.diff_charsToLines(diffs, tmpVector); - assertEquals("diff_charsToLines: More than 256.", diffList(Diff(DELETE, tmpVector.text1)), diffs); - } - - void testDiffCleanupMerge() { - // Cleanup a messy diff. - Diffs diffs; - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Null case.", diffList(), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "b"), Diff(INSERT, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: No change case.", diffList(Diff(EQUAL, "a"), Diff(DELETE, "b"), Diff(INSERT, "c")), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(EQUAL, "b"), Diff(EQUAL, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Merge equalities.", diffList(Diff(EQUAL, "abc")), diffs); - - diffs = diffList(Diff(DELETE, "a"), Diff(DELETE, "b"), Diff(DELETE, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Merge deletions.", diffList(Diff(DELETE, "abc")), diffs); - - diffs = diffList(Diff(INSERT, "a"), Diff(INSERT, "b"), Diff(INSERT, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Merge insertions.", diffList(Diff(INSERT, "abc")), diffs); - - diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, "b"), Diff(DELETE, "c"), Diff(INSERT, "d"), Diff(EQUAL, "e"), Diff(EQUAL, "f")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Merge interweave.", diffList(Diff(DELETE, "ac"), Diff(INSERT, "bd"), Diff(EQUAL, "ef")), diffs); - - diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, "abc"), Diff(DELETE, "dc")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Prefix and suffix detection.", diffList(Diff(EQUAL, "a"), Diff(DELETE, "d"), Diff(INSERT, "b"), Diff(EQUAL, "c")), diffs); - - diffs = diffList(Diff(EQUAL, "x"), Diff(DELETE, "a"), Diff(INSERT, "abc"), Diff(DELETE, "dc"), Diff(EQUAL, "y")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Prefix and suffix detection with equalities.", diffList(Diff(EQUAL, "xa"), Diff(DELETE, "d"), Diff(INSERT, "b"), Diff(EQUAL, "cy")), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(INSERT, "ba"), Diff(EQUAL, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Slide edit left.", diffList(Diff(INSERT, "ab"), Diff(EQUAL, "ac")), diffs); - - diffs = diffList(Diff(EQUAL, "c"), Diff(INSERT, "ab"), Diff(EQUAL, "a")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Slide edit right.", diffList(Diff(EQUAL, "ca"), Diff(INSERT, "ba")), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "b"), Diff(EQUAL, "c"), Diff(DELETE, "ac"), Diff(EQUAL, "x")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Slide edit left recursive.", diffList(Diff(DELETE, "abc"), Diff(EQUAL, "acx")), diffs); - - diffs = diffList(Diff(EQUAL, "x"), Diff(DELETE, "ca"), Diff(EQUAL, "c"), Diff(DELETE, "b"), Diff(EQUAL, "a")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Slide edit right recursive.", diffList(Diff(EQUAL, "xca"), Diff(DELETE, "cba")), diffs); - } - - void testDiffCleanupSemanticLossless() { - // Slide diffs to match logical boundaries. - Diffs diffs = diffList(); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Null case.", diffList(), diffs); - - diffs = diffList(Diff(EQUAL, "AAA\r\n\r\nBBB"), Diff(INSERT, "\r\nDDD\r\n\r\nBBB"), Diff(EQUAL, "\r\nEEE")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemanticLossless: Blank lines.", diffList(Diff(EQUAL, "AAA\r\n\r\n"), Diff(INSERT, "BBB\r\nDDD\r\n\r\n"), Diff(EQUAL, "BBB\r\nEEE")), diffs); - - diffs = diffList(Diff(EQUAL, "AAA\r\nBBB"), Diff(INSERT, " DDD\r\nBBB"), Diff(EQUAL, " EEE")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemanticLossless: Line boundaries.", diffList(Diff(EQUAL, "AAA\r\n"), Diff(INSERT, "BBB DDD\r\n"), Diff(EQUAL, "BBB EEE")), diffs); - - diffs = diffList(Diff(EQUAL, "The c"), Diff(INSERT, "ow and the c"), Diff(EQUAL, "at.")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Word boundaries.", diffList(Diff(EQUAL, "The "), Diff(INSERT, "cow and the "), Diff(EQUAL, "cat.")), diffs); - - diffs = diffList(Diff(EQUAL, "The-c"), Diff(INSERT, "ow-and-the-c"), Diff(EQUAL, "at.")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Alphanumeric boundaries.", diffList(Diff(EQUAL, "The-"), Diff(INSERT, "cow-and-the-"), Diff(EQUAL, "cat.")), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "a"), Diff(EQUAL, "ax")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Hitting the start.", diffList(Diff(DELETE, "a"), Diff(EQUAL, "aax")), diffs); - - diffs = diffList(Diff(EQUAL, "xa"), Diff(DELETE, "a"), Diff(EQUAL, "a")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Hitting the end.", diffList(Diff(EQUAL, "xaa"), Diff(DELETE, "a")), diffs); - - diffs = diffList(Diff(EQUAL, "The xxx. The "), Diff(INSERT, "zzz. The "), Diff(EQUAL, "yyy.")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Sentence boundaries.", diffList(Diff(EQUAL, "The xxx."), Diff(INSERT, " The zzz."), Diff(EQUAL, " The yyy.")), diffs); -} - - void testDiffCleanupSemantic() { - // Cleanup semantically trivial equalities. - Diffs diffs = diffList(); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Null case.", diffList(), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "cd"), Diff(EQUAL, "12"), Diff(DELETE, "e")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: No elimination #1.", diffList(Diff(DELETE, "ab"), Diff(INSERT, "cd"), Diff(EQUAL, "12"), Diff(DELETE, "e")), diffs); - - diffs = diffList(Diff(DELETE, "abc"), Diff(INSERT, "ABC"), Diff(EQUAL, "1234"), Diff(DELETE, "wxyz")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: No elimination #2.", diffList(Diff(DELETE, "abc"), Diff(INSERT, "ABC"), Diff(EQUAL, "1234"), Diff(DELETE, "wxyz")), diffs); - - diffs = diffList(Diff(DELETE, "a"), Diff(EQUAL, "b"), Diff(DELETE, "c")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Simple elimination.", diffList(Diff(DELETE, "abc"), Diff(INSERT, "b")), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(EQUAL, "cd"), Diff(DELETE, "e"), Diff(EQUAL, "f"), Diff(INSERT, "g")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Backpass elimination.", diffList(Diff(DELETE, "abcdef"), Diff(INSERT, "cdfg")), diffs); - - diffs = diffList(Diff(INSERT, "1"), Diff(EQUAL, "A"), Diff(DELETE, "B"), Diff(INSERT, "2"), Diff(EQUAL, "_"), Diff(INSERT, "1"), Diff(EQUAL, "A"), Diff(DELETE, "B"), Diff(INSERT, "2")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Multiple elimination.", diffList(Diff(DELETE, "AB_AB"), Diff(INSERT, "1A2_1A2")), diffs); - - diffs = diffList(Diff(EQUAL, "The c"), Diff(DELETE, "ow and the c"), Diff(EQUAL, "at.")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Word boundaries.", diffList(Diff(EQUAL, "The "), Diff(DELETE, "cow and the "), Diff(EQUAL, "cat.")), diffs); - - diffs = diffList(Diff(DELETE, "abcxx"), Diff(INSERT, "xxdef")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: No overlap elimination.", diffList(Diff(DELETE, "abcxx"), Diff(INSERT, "xxdef")), diffs); - - diffs = diffList(Diff(DELETE, "abcxxx"), Diff(INSERT, "xxxdef")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Overlap elimination.", diffList(Diff(DELETE, "abc"), Diff(EQUAL, "xxx"), Diff(INSERT, "def")), diffs); - - diffs = diffList(Diff(DELETE, "xxxabc"), Diff(INSERT, "defxxx")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Reverse overlap elimination.", diffList(Diff(INSERT, "def"), Diff(EQUAL, "xxx"), Diff(DELETE, "abc")), diffs); - - diffs = diffList(Diff(DELETE, "abcd1212"), Diff(INSERT, "1212efghi"), Diff(EQUAL, "----"), Diff(DELETE, "A3"), Diff(INSERT, "3BC")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Two overlap eliminations.", diffList(Diff(DELETE, "abcd"), Diff(EQUAL, "1212"), Diff(INSERT, "efghi"), Diff(EQUAL, "----"), Diff(DELETE, "A"), Diff(EQUAL, "3"), Diff(INSERT, "BC")), diffs); - } - - void testDiffCleanupEfficiency() { - // Cleanup operationally trivial equalities. - dmp.Diff_EditCost = 4; - Diffs diffs = diffList(); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: Null case.", diffList(), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "wxyz"), Diff(DELETE, "cd"), Diff(INSERT, "34")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: No elimination.", diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "wxyz"), Diff(DELETE, "cd"), Diff(INSERT, "34")), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "xyz"), Diff(DELETE, "cd"), Diff(INSERT, "34")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: Four-edit elimination.", diffList(Diff(DELETE, "abxyzcd"), Diff(INSERT, "12xyz34")), diffs); - - diffs = diffList(Diff(INSERT, "12"), Diff(EQUAL, "x"), Diff(DELETE, "cd"), Diff(INSERT, "34")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: Three-edit elimination.", diffList(Diff(DELETE, "xcd"), Diff(INSERT, "12x34")), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "xy"), Diff(INSERT, "34"), Diff(EQUAL, "z"), Diff(DELETE, "cd"), Diff(INSERT, "56")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: Backpass elimination.", diffList(Diff(DELETE, "abxyzcd"), Diff(INSERT, "12xy34z56")), diffs); - - dmp.Diff_EditCost = 5; - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "wxyz"), Diff(DELETE, "cd"), Diff(INSERT, "34")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: High cost elimination.", diffList(Diff(DELETE, "abwxyzcd"), Diff(INSERT, "12wxyz34")), diffs); - dmp.Diff_EditCost = 4; - } - - void testDiffPrettyHtml() { - // Pretty print. - Diffs diffs = diffList(Diff(EQUAL, "a\n"), Diff(DELETE, "b"), Diff(INSERT, "c&d")); - assertEquals("diff_prettyHtml:", "
<B>b</B>c&d", dmp.diff_prettyHtml(diffs)); - } - - void testDiffText() { - // Compute the source and destination texts. - Diffs diffs = diffList(Diff(EQUAL, "jump"), Diff(DELETE, "s"), Diff(INSERT, "ed"), Diff(EQUAL, " over "), Diff(DELETE, "the"), Diff(INSERT, "a"), Diff(EQUAL, " lazy")); - assertEquals("diff_text1:", "jumps over the lazy", dmp.diff_text1(diffs)); - assertEquals("diff_text2:", "jumped over a lazy", dmp.diff_text2(diffs)); - } - - void testDiffDelta() { - // Convert a diff into delta string. - Diffs diffs = diffList(Diff(EQUAL, "jump"), Diff(DELETE, "s"), Diff(INSERT, "ed"), Diff(EQUAL, " over "), Diff(DELETE, "the"), Diff(INSERT, "a"), Diff(EQUAL, " lazy"), Diff(INSERT, "old dog")); - string_t text1 = dmp.diff_text1(diffs); - assertEquals("diff_text1: Base text.", "jumps over the lazy", text1); - - string_t delta = dmp.diff_toDelta(diffs); - assertEquals("diff_toDelta:", "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", delta); - - // Convert delta string into a diff. - assertEquals("diff_fromDelta: Normal.", diffs, dmp.diff_fromDelta(text1, delta)); - - // Generates error (19 < 20). - try { - dmp.diff_fromDelta(text1 + "x", delta); - assertFalse("diff_fromDelta: Too long.", true); - } catch (string_t ex) { - // Exception expected. - } - - // Generates error (19 > 18). - try { - dmp.diff_fromDelta(text1.substr(1), delta); - assertFalse("diff_fromDelta: Too short.", true); - } catch (string_t ex) { - // Exception expected. - } - - // Generates error (%c3%xy invalid Unicode). - try { - dmp.diff_fromDelta("", "+%c3%xy"); - assertFalse("diff_fromDelta: Invalid character.", true); - } catch (string_t ex) { - // Exception expected. - } - -// TODO add again -// // Test deltas with special characters. -// diffs = diffList(Diff(EQUAL, string_t("\u0680 \000 \t %", 7)), Diff(DELETE, string_t("\u0681 \001 \n ^", 7)), Diff(INSERT, string_t("\u0682 \002 \\ |", 7))); -// text1 = dmp.diff_text1(diffs); -// assertEquals("diff_text1: Unicode text.", string_t(L"\u0680 \000 \t %\u0681 \001 \n ^", 14), text1); - -// delta = dmp.diff_toDelta(diffs); -// assertEquals("diff_toDelta: Unicode.", "=7\t-7\t+%DA%82 %02 %5C %7C", delta); - -// assertEquals("diff_fromDelta: Unicode.", diffs, dmp.diff_fromDelta(text1, delta)); - - // Verify pool of unchanged characters. - diffs = diffList(Diff(INSERT, "A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # ")); - string_t text2 = dmp.diff_text2(diffs); - assertEquals("diff_text2: Unchanged characters.", "A-Z a-z 0-9 - _ . ! ~ * \' ( ) ; / ? : @ & = + $ , # ", text2); - - delta = dmp.diff_toDelta(diffs); - assertEquals("diff_toDelta: Unchanged characters.", "+A-Z a-z 0-9 - _ . ! ~ * \' ( ) ; / ? : @ & = + $ , # ", delta); - - // Convert delta string into a diff. - assertEquals("diff_fromDelta: Unchanged characters.", diffs, dmp.diff_fromDelta("", delta)); - } - - void testDiffXIndex() { - // Translate a location in text1 to text2. - Diffs diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, "1234"), Diff(EQUAL, "xyz")); - assertEquals("diff_xIndex: Translation on equality.", 5, dmp.diff_xIndex(diffs, 2)); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "1234"), Diff(EQUAL, "xyz")); - assertEquals("diff_xIndex: Translation on deletion.", 1, dmp.diff_xIndex(diffs, 3)); - } - - void testDiffLevenshtein() { - Diffs diffs = diffList(Diff(DELETE, "abc"), Diff(INSERT, "1234"), Diff(EQUAL, "xyz")); - assertEquals("diff_levenshtein: Trailing equality.", 4, dmp.diff_levenshtein(diffs)); - - diffs = diffList(Diff(EQUAL, "xyz"), Diff(DELETE, "abc"), Diff(INSERT, "1234")); - assertEquals("diff_levenshtein: Leading equality.", 4, dmp.diff_levenshtein(diffs)); - - diffs = diffList(Diff(DELETE, "abc"), Diff(EQUAL, "xyz"), Diff(INSERT, "1234")); - assertEquals("diff_levenshtein: Middle equality.", 7, dmp.diff_levenshtein(diffs)); - } - - void testDiffBisect() { - // Normal. - string_t a = "cat"; - string_t b = "map"; - // Since the resulting diff hasn't been normalized, it would be ok if - // the insertion and deletion pairs are swapped. - // If the order changes, tweak this test as required. - Diffs diffs = diffList(Diff(DELETE, "c"), Diff(INSERT, "m"), Diff(EQUAL, "a"), Diff(DELETE, "t"), Diff(INSERT, "p")); - assertEquals("diff_bisect: Normal.", diffs, dmp.diff_bisect(a, b, numeric_limits::max())); - - // Timeout. - while (clock() == 0); // Wait for the first tick - diffs = diffList(Diff(DELETE, "cat"), Diff(INSERT, "map")); - assertEquals("diff_bisect: Timeout.", diffs, dmp.diff_bisect(a, b, 0)); - } - - void testDiffMain() { - // Perform a trivial diff. - Diffs diffs = diffList(); - assertEquals("diff_main: Null case.", diffs, dmp.diff_main("", "", false)); - - diffs = diffList(Diff(EQUAL, "abc")); - assertEquals("diff_main: Equality.", diffs, dmp.diff_main("abc", "abc", false)); - - diffs = diffList(Diff(EQUAL, "ab"), Diff(INSERT, "123"), Diff(EQUAL, "c")); - assertEquals("diff_main: Simple insertion.", diffs, dmp.diff_main("abc", "ab123c", false)); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "123"), Diff(EQUAL, "bc")); - assertEquals("diff_main: Simple deletion.", diffs, dmp.diff_main("a123bc", "abc", false)); - - diffs = diffList(Diff(EQUAL, "a"), Diff(INSERT, "123"), Diff(EQUAL, "b"), Diff(INSERT, "456"), Diff(EQUAL, "c")); - assertEquals("diff_main: Two insertions.", diffs, dmp.diff_main("abc", "a123b456c", false)); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "123"), Diff(EQUAL, "b"), Diff(DELETE, "456"), Diff(EQUAL, "c")); - assertEquals("diff_main: Two deletions.", diffs, dmp.diff_main("a123b456c", "abc", false)); - - // Perform a real diff. - // Switch off the timeout. - dmp.Diff_Timeout = 0; - diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, "b")); - assertEquals("diff_main: Simple case #1.", diffs, dmp.diff_main("a", "b", false)); - - diffs = diffList(Diff(DELETE, "Apple"), Diff(INSERT, "Banana"), Diff(EQUAL, "s are a"), Diff(INSERT, "lso"), Diff(EQUAL, " fruit.")); - assertEquals("diff_main: Simple case #2.", diffs, dmp.diff_main("Apples are a fruit.", "Bananas are also fruit.", false)); - -// diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, L"\u0680"), Diff(EQUAL, "x"), Diff(DELETE, "\t"), Diff(INSERT, string_t("\000", 1))); -// assertEquals("diff_main: Simple case #3.", diffs, dmp.diff_main("ax\t", string_t(L"\u0680x\000", 3), false)); - - diffs = diffList(Diff(DELETE, "1"), Diff(EQUAL, "a"), Diff(DELETE, "y"), Diff(EQUAL, "b"), Diff(DELETE, "2"), Diff(INSERT, "xab")); - assertEquals("diff_main: Overlap #1.", diffs, dmp.diff_main("1ayb2", "abxab", false)); - - diffs = diffList(Diff(INSERT, "xaxcx"), Diff(EQUAL, "abc"), Diff(DELETE, "y")); - assertEquals("diff_main: Overlap #2.", diffs, dmp.diff_main("abcy", "xaxcxabc", false)); - - diffs = diffList(Diff(DELETE, "ABCD"), Diff(EQUAL, "a"), Diff(DELETE, "="), Diff(INSERT, "-"), Diff(EQUAL, "bcd"), Diff(DELETE, "="), Diff(INSERT, "-"), Diff(EQUAL, "efghijklmnopqrs"), Diff(DELETE, "EFGHIJKLMNOefg")); - assertEquals("diff_main: Overlap #3.", diffs, dmp.diff_main("ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", "a-bcd-efghijklmnopqrs", false)); - - diffs = diffList(Diff(INSERT, " "), Diff(EQUAL, "a"), Diff(INSERT, "nd"), Diff(EQUAL, " [[Pennsylvania]]"), Diff(DELETE, " and [[New")); - assertEquals("diff_main: Large equality.", diffs, dmp.diff_main("a [[Pennsylvania]] and [[New", " and [[Pennsylvania]]", false)); - - dmp.Diff_Timeout = 0.1f; // 100ms - // This test may 'fail' on extremely fast computers. If so, just increase the text lengths. - string_t a = "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n"; - string_t b = "I am the very model of a modern major general,\nI've information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n"; - // Increase the text lengths by 1024 times to ensure a timeout. - for (int x = 0; x < 10; x++) { - a = a + a; - b = b + b; - } - clock_t startTime = clock(); - dmp.diff_main(a, b); - clock_t endTime = clock(); - // Test that we took at least the timeout period. - assertTrue("diff_main: Timeout min.", dmp.Diff_Timeout * CLOCKS_PER_SEC <= endTime - startTime); - // Test that we didn't take forever (be forgiving). - // Theoretically this test could fail very occasionally if the - // OS task swaps or locks up for a second at the wrong moment. - // Java seems to overrun by ~80% (compared with 10% for other languages). - // Therefore use an upper limit of 0.5s instead of 0.2s. - assertTrue("diff_main: Timeout max.", dmp.Diff_Timeout * CLOCKS_PER_SEC * 2 > endTime - startTime); - dmp.Diff_Timeout = 0; - - // Test the linemode speedup. - // Must be long to pass the 100 char cutoff. - a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n"; - b = "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n"; - assertEquals("diff_main: Simple line-mode.", dmp.diff_main(a, b, true), dmp.diff_main(a, b, false)); - - a = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; - b = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"; - assertEquals("diff_main: Single line-mode.", dmp.diff_main(a, b, true), dmp.diff_main(a, b, false)); - - a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n"; - b = "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n"; - Strings texts_linemode = diff_rebuildtexts(dmp.diff_main(a, b, true)); - Strings texts_textmode = diff_rebuildtexts(dmp.diff_main(a, b, false)); - assertEquals("diff_main: Overlap line-mode.", texts_textmode, texts_linemode); - - // Test null inputs -- not needed because nulls can't be passed in string_t&. - } - - - // MATCH TEST FUNCTIONS - - void testMatchAlphabet() { - // Initialise the bitmasks for Bitap. - map bitmask, bitmask2; - bitmask['a'] = 4; - bitmask['b'] = 2; - bitmask['c'] = 1; - dmp.match_alphabet("abc", bitmask2); - assertEquals("match_alphabet: Unique.", bitmask, bitmask2); - - bitmask.clear(); - bitmask['a'] = 37; - bitmask['b'] = 18; - bitmask['c'] = 8; - bitmask2.clear(); - dmp.match_alphabet("abcaba", bitmask2); - assertEquals("match_alphabet: Duplicates.", bitmask, bitmask2); - } - - void testMatchBitap() { - // Bitap algorithm. - dmp.Match_Distance = 100; - dmp.Match_Threshold = 0.5f; - assertEquals("match_bitap: Exact match #1.", 5, dmp.match_bitap("abcdefghijk", "fgh", 5)); - - assertEquals("match_bitap: Exact match #2.", 5, dmp.match_bitap("abcdefghijk", "fgh", 0)); - - assertEquals("match_bitap: Fuzzy match #1.", 4, dmp.match_bitap("abcdefghijk", "efxhi", 0)); - - assertEquals("match_bitap: Fuzzy match #2.", 2, dmp.match_bitap("abcdefghijk", "cdefxyhijk", 5)); - - assertEquals("match_bitap: Fuzzy match #3.", -1, dmp.match_bitap("abcdefghijk", "bxy", 1)); - - assertEquals("match_bitap: Overflow.", 2, dmp.match_bitap("123456789xx0", "3456789x0", 2)); - - assertEquals("match_bitap: Before start match.", 0, dmp.match_bitap("abcdef", "xxabc", 4)); - - assertEquals("match_bitap: Beyond end match.", 3, dmp.match_bitap("abcdef", "defyy", 4)); - - assertEquals("match_bitap: Oversized pattern.", 0, dmp.match_bitap("abcdef", "xabcdefy", 0)); - - dmp.Match_Threshold = 0.4f; - assertEquals("match_bitap: Threshold #1.", 4, dmp.match_bitap("abcdefghijk", "efxyhi", 1)); - - dmp.Match_Threshold = 0.3f; - assertEquals("match_bitap: Threshold #2.", -1, dmp.match_bitap("abcdefghijk", "efxyhi", 1)); - - dmp.Match_Threshold = 0.0f; - assertEquals("match_bitap: Threshold #3.", 1, dmp.match_bitap("abcdefghijk", "bcdef", 1)); - - dmp.Match_Threshold = 0.5f; - assertEquals("match_bitap: Multiple select #1.", 0, dmp.match_bitap("abcdexyzabcde", "abccde", 3)); - - assertEquals("match_bitap: Multiple select #2.", 8, dmp.match_bitap("abcdexyzabcde", "abccde", 5)); - - dmp.Match_Distance = 10; // Strict location. - assertEquals("match_bitap: Distance test #1.", -1, dmp.match_bitap("abcdefghijklmnopqrstuvwxyz", "abcdefg", 24)); - - assertEquals("match_bitap: Distance test #2.", 0, dmp.match_bitap("abcdefghijklmnopqrstuvwxyz", "abcdxxefg", 1)); - - dmp.Match_Distance = 1000; // Loose location. - assertEquals("match_bitap: Distance test #3.", 0, dmp.match_bitap("abcdefghijklmnopqrstuvwxyz", "abcdefg", 24)); - } - - void testMatchMain() { - // Full match. - assertEquals("match_main: Equality.", 0, dmp.match_main("abcdef", "abcdef", 1000)); - - assertEquals("match_main: Null text.", -1, dmp.match_main("", "abcdef", 1)); - - assertEquals("match_main: Null pattern.", 3, dmp.match_main("abcdef", "", 3)); - - assertEquals("match_main: Exact match.", 3, dmp.match_main("abcdef", "de", 3)); - - dmp.Match_Threshold = 0.7f; - assertEquals("match_main: Complex match.", 4, dmp.match_main("I am the very model of a modern major general.", " that berry ", 5)); - dmp.Match_Threshold = 0.5f; - - // Test null inputs -- not needed because nulls can't be passed in string_t&. - } - - - // PATCH TEST FUNCTIONS - - void testPatchObj() { - // Patch Object. - Patch p; - p.start1 = 20; - p.start2 = 21; - p.length1 = 18; - p.length2 = 17; - p.diffs = diffList(Diff(EQUAL, "jump"), Diff(DELETE, "s"), Diff(INSERT, "ed"), Diff(EQUAL, " over "), Diff(DELETE, "the"), Diff(INSERT, "a"), Diff(EQUAL, "\nlaz")); - string_t strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n"; - assertEquals("Patch: toString.", strp, p.toString()); - } - - void testPatchFromText() { - assertTrue("patch_fromText: #0.", dmp.patch_fromText("").empty()); - - string_t strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n"; - assertEquals("patch_fromText: #1.", strp, dmp.patch_fromText(strp).front().toString()); - - assertEquals("patch_fromText: #2.", "@@ -1 +1 @@\n-a\n+b\n", dmp.patch_fromText("@@ -1 +1 @@\n-a\n+b\n").front().toString()); - - assertEquals("patch_fromText: #3.", "@@ -1,3 +0,0 @@\n-abc\n", dmp.patch_fromText("@@ -1,3 +0,0 @@\n-abc\n").front().toString()); - - assertEquals("patch_fromText: #4.", "@@ -0,0 +1,3 @@\n+abc\n", dmp.patch_fromText("@@ -0,0 +1,3 @@\n+abc\n").front().toString()); - - // Generates error. - try { - dmp.patch_fromText("Bad\nPatch\n"); - assertFalse("patch_fromText: #5.", true); - } catch (string_t ex) { - // Exception expected. - } - } - - void testPatchToText() { - string_t strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"; - Patches patches; - patches = dmp.patch_fromText(strp); - assertEquals("patch_toText: Single", strp, dmp.patch_toText(patches)); - - strp = "@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n"; - patches = dmp.patch_fromText(strp); - assertEquals("patch_toText: Dua", strp, dmp.patch_toText(patches)); - } - - void testPatchAddContext() { - dmp.Patch_Margin = 4; - Patch p; - p = dmp.patch_fromText("@@ -21,4 +21,10 @@\n-jump\n+somersault\n").front(); - dmp.patch_addContext(p, "The quick brown fox jumps over the lazy dog."); - assertEquals("patch_addContext: Simple case.", "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n", p.toString()); - - p = dmp.patch_fromText("@@ -21,4 +21,10 @@\n-jump\n+somersault\n").front(); - dmp.patch_addContext(p, "The quick brown fox jumps."); - assertEquals("patch_addContext: Not enough trailing context.", "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n", p.toString()); - - p = dmp.patch_fromText("@@ -3 +3,2 @@\n-e\n+at\n").front(); - dmp.patch_addContext(p, "The quick brown fox jumps."); - assertEquals("patch_addContext: Not enough leading context.", "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n qui\n", p.toString()); - - p = dmp.patch_fromText("@@ -3 +3,2 @@\n-e\n+at\n").front(); - dmp.patch_addContext(p, "The quick brown fox jumps. The quick brown fox crashes."); - assertEquals("patch_addContext: Ambiguity.", "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n quick brown fox jumps. \n", p.toString()); - } - - void testPatchMake() { - Patches patches; - patches = dmp.patch_make("", ""); - assertEquals("patch_make: Null case", "", dmp.patch_toText(patches)); - - string_t text1 = "The quick brown fox jumps over the lazy dog."; - string_t text2 = "That quick brown fox jumped over a lazy dog."; - string_t expectedPatch = "@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n"; - // The second patch must be "-21,17 +21,18", not "-22,17 +21,18" due to rolling context. - patches = dmp.patch_make(text2, text1); - assertEquals("patch_make: Text2+Text1 inputs", expectedPatch, dmp.patch_toText(patches)); - - expectedPatch = "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"; - patches = dmp.patch_make(text1, text2); - assertEquals("patch_make: Text1+Text2 inputs", expectedPatch, dmp.patch_toText(patches)); - - Diffs diffs = dmp.diff_main(text1, text2, false); - patches = dmp.patch_make(diffs); - assertEquals("patch_make: Diff input", expectedPatch, dmp.patch_toText(patches)); - - patches = dmp.patch_make(text1, diffs); - assertEquals("patch_make: Text1+Diff inputs", expectedPatch, dmp.patch_toText(patches)); - - patches = dmp.patch_make(text1, text2, diffs); - assertEquals("patch_make: Text1+Text2+Diff inputs (deprecated)", expectedPatch, dmp.patch_toText(patches)); - - patches = dmp.patch_make("`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?"); - assertEquals("patch_toText: Character encoding.", "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n", dmp.patch_toText(patches)); - - diffs = diffList(Diff(DELETE, "`1234567890-=[]\\;',./"), Diff(INSERT, "~!@#$%^&*()_+{}|:\"<>?")); - assertEquals("patch_fromText: Character decoding.", diffs, dmp.patch_fromText("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n").front().diffs); - - text1.clear(); - for (int x = 0; x < 100; x++) { - text1 += "abcdef"; - } - text2 = text1 + "123"; - expectedPatch = "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n"; - patches = dmp.patch_make(text1, text2); - assertEquals("patch_make: Long string with repeats.", expectedPatch, dmp.patch_toText(patches)); - - // Test null inputs -- not needed because nulls can't be passed in string_t&. - } - - void testPatchSplitMax() { - // Assumes that Match_MaxBits is 32. - Patches patches; - patches = dmp.patch_make("abcdefghijklmnopqrstuvwxyz01234567890", "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0"); - dmp.patch_splitMax(patches); - assertEquals("patch_splitMax: #1.", "@@ -1,32 +1,46 @@\n+X\n ab\n+X\n cd\n+X\n ef\n+X\n gh\n+X\n ij\n+X\n kl\n+X\n mn\n+X\n op\n+X\n qr\n+X\n st\n+X\n uv\n+X\n wx\n+X\n yz\n+X\n 012345\n@@ -25,13 +39,18 @@\n zX01\n+X\n 23\n+X\n 45\n+X\n 67\n+X\n 89\n+X\n 0\n", dmp.patch_toText(patches)); - - patches = dmp.patch_make("abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", "abcdefuvwxyz"); - string_t oldToText = dmp.patch_toText(patches); - dmp.patch_splitMax(patches); - assertEquals("patch_splitMax: #2.", oldToText, dmp.patch_toText(patches)); - - patches = dmp.patch_make("1234567890123456789012345678901234567890123456789012345678901234567890", "abc"); - dmp.patch_splitMax(patches); - assertEquals("patch_splitMax: #3.", "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n@@ -29,32 +1,4 @@\n-9012345678901234567890123456\n 7890\n@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n", dmp.patch_toText(patches)); - - patches = dmp.patch_make("abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1", "abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1"); - dmp.patch_splitMax(patches); - assertEquals("patch_splitMax: #4.", "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n@@ -29,32 +29,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n", dmp.patch_toText(patches)); - } - - void testPatchAddPadding() { - Patches patches; - patches = dmp.patch_make("", "test"); - assertEquals("patch_addPadding: Both edges full.", "@@ -0,0 +1,4 @@\n+test\n", dmp.patch_toText(patches)); - dmp.patch_addPadding(patches); - assertEquals("patch_addPadding: Both edges full.", "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n", dmp.patch_toText(patches)); - - patches = dmp.patch_make("XY", "XtestY"); - assertEquals("patch_addPadding: Both edges partial.", "@@ -1,2 +1,6 @@\n X\n+test\n Y\n", dmp.patch_toText(patches)); - dmp.patch_addPadding(patches); - assertEquals("patch_addPadding: Both edges partial.", "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n", dmp.patch_toText(patches)); - - patches = dmp.patch_make("XXXXYYYY", "XXXXtestYYYY"); - assertEquals("patch_addPadding: Both edges none.", "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n", dmp.patch_toText(patches)); - dmp.patch_addPadding(patches); - assertEquals("patch_addPadding: Both edges none.", "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n", dmp.patch_toText(patches)); - } - - void testPatchApply() { - dmp.Match_Distance = 1000; - dmp.Match_Threshold = 0.5f; - dmp.Patch_DeleteThreshold = 0.5f; - Patches patches; - patches = dmp.patch_make("", ""); - pair > results = dmp.patch_apply(patches, "Hello world."); - vector boolArray = results.second; - - basic_stringstream result; - result << results.first << traits::tab << boolArray.size(); - assertEquals("patch_apply: Null case.", "Hello world.\t0", result.str()); - - string_t resultStr; - patches = dmp.patch_make("The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog."); - results = dmp.patch_apply(patches, "The quick brown fox jumps over the lazy dog."); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Exact match.", "That quick brown fox jumped over a lazy dog.\ttrue\ttrue", resultStr); - - results = dmp.patch_apply(patches, "The quick red rabbit jumps over the tired tiger."); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Partial match.", "That quick red rabbit jumped over a tired tiger.\ttrue\ttrue", resultStr); - - results = dmp.patch_apply(patches, "I am the very model of a modern major general."); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Failed match.", "I am the very model of a modern major general.\tfalse\tfalse", resultStr); - - patches = dmp.patch_make("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy"); - results = dmp.patch_apply(patches, "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Big delete, small change.", "xabcy\ttrue\ttrue", resultStr); - - patches = dmp.patch_make("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy"); - results = dmp.patch_apply(patches, "x12345678901234567890---------------++++++++++---------------12345678901234567890y"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Big delete, large change 1.", "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\tfalse\ttrue", resultStr); - - dmp.Patch_DeleteThreshold = 0.6f; - patches = dmp.patch_make("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy"); - results = dmp.patch_apply(patches, "x12345678901234567890---------------++++++++++---------------12345678901234567890y"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Big delete, large change 2.", "xabcy\ttrue\ttrue", resultStr); - dmp.Patch_DeleteThreshold = 0.5f; - - dmp.Match_Threshold = 0.0f; - dmp.Match_Distance = 0; - patches = dmp.patch_make("abcdefghijklmnopqrstuvwxyz--------------------1234567890", "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890"); - results = dmp.patch_apply(patches, "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Compensate for failed patch.", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\tfalse\ttrue", resultStr); - dmp.Match_Threshold = 0.5f; - dmp.Match_Distance = 1000; - - patches = dmp.patch_make("", "test"); - string_t patchStr = dmp.patch_toText(patches); - dmp.patch_apply(patches, ""); - assertEquals("patch_apply: No side effects.", patchStr, dmp.patch_toText(patches)); - - patches = dmp.patch_make("The quick brown fox jumps over the lazy dog.", "Woof"); - patchStr = dmp.patch_toText(patches); - dmp.patch_apply(patches, "The quick brown fox jumps over the lazy dog."); - assertEquals("patch_apply: No side effects with major delete.", patchStr, dmp.patch_toText(patches)); - - patches = dmp.patch_make("", "test"); - results = dmp.patch_apply(patches, ""); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false"); - assertEquals("patch_apply: Edge exact match.", "test\ttrue", resultStr); - - patches = dmp.patch_make("XY", "XtestY"); - results = dmp.patch_apply(patches, "XY"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false"); - assertEquals("patch_apply: Near edge exact match.", "XtestY\ttrue", resultStr); - - patches = dmp.patch_make("y", "y123"); - results = dmp.patch_apply(patches, "x"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false"); - assertEquals("patch_apply: Edge partial match.", "x123\ttrue", resultStr); - } - - private: - // Define equality. - void assertEquals(const char* strCase, int n1, int n2) { - if (n1 != n2) { - cerr << strCase << " FAIL\nExpected: " << n1 << "\nActual: " << n2 << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const string_t &s1, const string_t &s2) { - if (s1 != s2) { - cerr << strCase << " FAIL\nExpected: " << s1 << "\nActual: " << s2 << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const Diff &d1, const Diff &d2) { - if (d1 != d2) { - cerr << strCase << " FAIL\nExpected: " << d1.toString() << "\nActual: " << d2.toString() << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const Diffs &list1, const Diffs &list2) { - bool fail = false; - if (list1.size() == list2.size()) { - for (Diffs::const_iterator d1 = list1.begin(), d2 = list2.begin(); d1 != list1.end(); ++d1, ++d2) { - if (*d1 != *d2) { - fail = true; - break; - } - } - } else { - fail = true; - } - - if (fail) { - // Build human readable description of both lists. - string_t listString1 = "("; - bool first = true; - for (Diffs::const_iterator d1 = list1.begin(); d1 != list1.end(); ++d1) { - if (!first) { - listString1 += ", "; - } - listString1 += (*d1).toString(); - first = false; - } - listString1 += ")"; - string_t listString2 = "("; - first = true; - for (Diffs::const_iterator d2 = list2.begin(); d2 != list2.end(); ++d2) { - if (!first) { - listString2 += ", "; - } - listString2 += (*d2).toString(); - first = false; - } - listString2 += ")"; - cerr << strCase << " FAIL\nExpected: " << listString1 << "\nActual: " << listString2 << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const string_t& text1_1, const string_t& text2_1, const Lines &lines1, - const string_t& text1_2, const string_t& text2_2, const Lines &lines2) - { - bool fail = false; - if (text1_1 != text1_2 || text2_1 != text2_2 || lines1.size() != lines2.size()) - fail = true; - else - for (Lines::const_iterator i = lines1.begin(), j = lines2.begin(); i != lines1.end(); ++i, ++j) - if (string_t((*i).first, (*i).second) != string_t((*j).first, (*j).second)) { fail = true; break; } - - if (fail) { - // Build human readable description of both lists. - string_t listString1 = "\"" + text1_1 + "\", \"" + text2_1 + "\", (\""; - bool first = true; - for (Lines::const_iterator i = lines1.begin() + 1; i != lines1.end(); ++i) { - if (!first) listString1 += "\", \""; - listString1.append((*i).first, (*i).second); - first = false; - } - listString1 += "\")"; - string_t listString2 = "\"" + text1_2 + "\", \"" + text2_2 + "\", (\""; - first = true; - for (Lines::const_iterator j = lines2.begin() + 1; j != lines2.end(); ++j) { - if (!first) listString2 += "\", \""; - listString2.append((*j).first, (*j).second); - first = false; - } - listString2 += "\")"; - cerr << strCase << " FAIL\nExpected: " << listString1 << "\nActual: " << listString2 << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const map &m1, const map &m2) { - map::const_iterator i1 = m1.begin(), i2 = m2.begin(); - - while (i1 != m1.end() && i2 != m2.end()) { - if ((*i1).first != (*i2).first || (*i1).second != (*i2).second) { - cerr << strCase << " FAIL\nExpected: (" << char((*i1).first) << ", " << (*i1).second << - ")\nActual: (" << char((*i2).first) << ", " << (*i2).second << ')' << endl; - throw strCase; - } - ++i1, ++i2; - } - - if (i1 != m1.end()) { - cerr << strCase << " FAIL\nExpected: (" << char((*i1).first) << ", " << (*i1).second << ")\nActual: none" << endl; - throw strCase; - } - if (i2 != m2.end()) { - cerr << strCase << " FAIL\nExpected: none\nActual: (" << char((*i2).first) << ", " << (*i2).second << ')' << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const Strings &list1, const Strings &list2) { - if (list1 != list2) { - cerr << strCase << " FAIL\nExpected: " << join(list1, ',') << "\nActual: " << join(list2, ',') << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertTrue(const char* strCase, bool value) { - if (!value) { - cerr << strCase << " FAIL\nExpected: true\nActual: false" << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertFalse(const char* strCase, bool value) { - if (value) { - cerr << strCase << " FAIL\nExpected: false\nActual: true" << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEmpty(const char* strCase, const Strings &list) { - if (!list.empty()) { - throw strCase; - } - } - - // Construct the two texts which made up the diff originally. - static Strings diff_rebuildtexts(Diffs diffs) { - Strings text; - text.push_back(string_t()); - text.push_back(string_t()); - Strings::iterator t1 = text.begin(), t0 = t1++; - for (Diffs::const_iterator myDiff = diffs.begin(); myDiff != diffs.end(); ++myDiff) { - if ((*myDiff).operation != INSERT) { - *t0 += (*myDiff).text; - } - if ((*myDiff).operation != DELETE) { - *t1 += (*myDiff).text; - } - } - return text; - } - - // Private function for quickly building lists of diffs. - static Diffs diffList( - // Diff(INSERT, NULL) is invalid and thus is used as the default argument. - Diff d1 = Diff(INSERT, string_t()), Diff d2 = Diff(INSERT, string_t()), - Diff d3 = Diff(INSERT, string_t()), Diff d4 = Diff(INSERT, string_t()), - Diff d5 = Diff(INSERT, string_t()), Diff d6 = Diff(INSERT, string_t()), - Diff d7 = Diff(INSERT, string_t()), Diff d8 = Diff(INSERT, string_t()), - Diff d9 = Diff(INSERT, string_t()), Diff d10 = Diff(INSERT, string_t())) { - // Diff(INSERT, NULL) is invalid and thus is used as the default argument. - Diffs listRet; - if (d1.operation == INSERT && d1.text.empty()) { - return listRet; - } - listRet.push_back(d1); - - if (d2.operation == INSERT && d2.text.empty()) { - return listRet; - } - listRet.push_back(d2); - - if (d3.operation == INSERT && d3.text.empty()) { - return listRet; - } - listRet.push_back(d3); - - if (d4.operation == INSERT && d4.text.empty()) { - return listRet; - } - listRet.push_back(d4); - - if (d5.operation == INSERT && d5.text.empty()) { - return listRet; - } - listRet.push_back(d5); - - if (d6.operation == INSERT && d6.text.empty()) { - return listRet; - } - listRet.push_back(d6); - - if (d7.operation == INSERT && d7.text.empty()) { - return listRet; - } - listRet.push_back(d7); - - if (d8.operation == INSERT && d8.text.empty()) { - return listRet; - } - listRet.push_back(d8); - - if (d9.operation == INSERT && d9.text.empty()) { - return listRet; - } - listRet.push_back(d9); - - if (d10.operation == INSERT && d10.text.empty()) { - return listRet; - } - listRet.push_back(d10); - - return listRet; - } - - Strings diff_halfMatch(const string_t &text1, const string_t &text2) { - Strings list; - HalfMatchResult hm; - if (diff_match_patch::diff_halfMatch(text1, text2, hm)) { - list.push_back(hm.text1_a); - list.push_back(hm.text1_b); - list.push_back(hm.text2_a); - list.push_back(hm.text2_b); - list.push_back(hm.mid_common); - } - return list; - } - - static string_t join(const Strings &list, char delim) { - string_t s; - for (Strings::const_iterator i = list.begin(); i != list.end(); ++i) { - if (i != list.begin()) s += delim; - s += *i; - } - return s; - } - - static Strings split(const string_t& str, char delim) { - Strings list; - string_t::size_type token_len; - for (string_t::const_pointer token = str.c_str(), end = token + str.length(), p; token < end; token += token_len + 1) { - for (p = token; p != end; ++p) if (*p == delim) break; - list.push_back(string_t(token, token_len = p - token)); - } - return list; - } -}; - - -int main() { - diff_match_patch_test dmp_test; - cout << "Starting diff_match_patch unit tests." << endl; - dmp_test.run_all_tests(); - cout << "Done." << endl; - return 0; -} - - -/* -Compile instructions for MinGW on Windows: -g++ -O2 -o diff_match_patch_test diff_match_patch_test.cpp -diff_match_patch_test.exe -*/ diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch_test_wstring.cpp b/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch_test_wstring.cpp deleted file mode 100644 index 08b3915c2a..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/diff_match_patch_test_wstring.cpp +++ /dev/null @@ -1,1243 +0,0 @@ -/* - * Copyright 2008 Google Inc. All Rights Reserved. - * Author: fraser@google.com (Neil Fraser) - * Author: mikeslemmer@gmail.com (Mike Slemmer) - * Author: snhere@gmail.com (Sergey Nozhenko) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Diff Match and Patch -- Test Harness using std::wstring - * http://code.google.com/p/google-diff-match-patch/ - */ - -#include "diff_match_patch.h" -#include -#include -#include -#include - -using namespace std; - -struct wastring : wstring // The same as wstring, but can be constructed from char* (to use ASCII strings in the test functions) -{ - wastring() {} - wastring(const wstring& s) : wstring(s) {} - wastring(const value_type* s) : wstring(s) {} - wastring(const value_type* s, size_t n) : wstring(s, n) {} - wastring(const char* s) : wstring(s, s + strlen(s)) {} - wastring(const char* s, size_t n) : wstring(s, s + n) {} - wastring operator+=(value_type c) { append(1, c); return *this; } - wastring operator+=(const wastring& s) { append(s); return *this; } - wastring operator+=(const char* s) { append(s, s + strlen(s)); return *this; } - - static const wchar_t eol = L'\n'; - static const wchar_t tab = L'\t'; -}; - -inline wastring operator+(const wastring& s, const char* p) { return s + wastring(p); } -inline wastring operator+(const char* p, const wastring& s) { return wastring(p) + s; } - -ostream& operator<<(ostream& o, const wastring& s) -{ - o << setfill('0'); - for (wastring::const_pointer p = s.c_str(), end = p + s.length(); p != end; ++p) - { - if (*p >= 0x80) - o << "\\u" << hex << setw(4) << unsigned(*p); - else if (*p >= ' ') - o << char(*p); - else - switch (char(*p)) { - case '\n': o << "\\n"; break; - case '\r': o << "\\r"; break; - case '\t': o << "\\t"; break; - default: o << "\\x" << hex << setw(2) << unsigned(*p); - } - } - return o; -} - - -#define dmp (*this) - -class diff_match_patch_test : diff_match_patch { - typedef vector Strings; - typedef diff_match_patch_traits traits; - public: - diff_match_patch_test() {} - - - void run_all_tests() { - clock_t t = clock(); - try { - testDiffCommonPrefix(); - testDiffCommonSuffix(); - testDiffCommonOverlap(); - testDiffHalfmatch(); - testDiffLinesToChars(); - testDiffCharsToLines(); - testDiffCleanupMerge(); - testDiffCleanupSemanticLossless(); - testDiffCleanupSemantic(); - testDiffCleanupEfficiency(); - testDiffPrettyHtml(); - testDiffText(); - testDiffDelta(); - testDiffXIndex(); - testDiffLevenshtein(); - testDiffBisect(); - testDiffMain(); - - testMatchAlphabet(); - testMatchBitap(); - testMatchMain(); - - testPatchObj(); - testPatchFromText(); - testPatchToText(); - testPatchAddContext(); - testPatchMake(); - testPatchSplitMax(); - testPatchAddPadding(); - testPatchApply(); - - cout << "All tests passed." << endl; - } catch (const char* strCase) { - cerr << "Test failed: " << strCase << endl; - } - cout << "Total time: " << int((clock() - t) * 1000 / CLOCKS_PER_SEC) << " ms" << endl; - } - - - // DIFF TEST FUNCTIONS - - void testDiffCommonPrefix() { - // Detect any common prefix. - assertEquals("diff_commonPrefix: Null case.", 0, dmp.diff_commonPrefix("abc", "xyz")); - - assertEquals("diff_commonPrefix: Non-null case.", 4, dmp.diff_commonPrefix("1234abcdef", "1234xyz")); - - assertEquals("diff_commonPrefix: Whole case.", 4, dmp.diff_commonPrefix("1234", "1234xyz")); - } - - void testDiffCommonSuffix() { - // Detect any common suffix. - assertEquals("diff_commonSuffix: Null case.", 0, dmp.diff_commonSuffix("abc", "xyz")); - - assertEquals("diff_commonSuffix: Non-null case.", 4, dmp.diff_commonSuffix("abcdef1234", "xyz1234")); - - assertEquals("diff_commonSuffix: Whole case.", 4, dmp.diff_commonSuffix("1234", "xyz1234")); - } - - void testDiffCommonOverlap() { - // Detect any suffix/prefix overlap. - assertEquals("diff_commonOverlap: Null case.", 0, dmp.diff_commonOverlap("", "abcd")); - - assertEquals("diff_commonOverlap: Whole case.", 3, dmp.diff_commonOverlap("abc", "abcd")); - - assertEquals("diff_commonOverlap: No overlap.", 0, dmp.diff_commonOverlap("123456", "abcd")); - - assertEquals("diff_commonOverlap: Overlap.", 3, dmp.diff_commonOverlap("123456xxx", "xxxabcd")); - - // Some overly clever languages (C#) may treat ligatures as equal to their - // component letters. E.g. U+FB01 == 'fi' - assertEquals("diff_commonOverlap: Unicode.", 0, dmp.diff_commonOverlap("fi", L"\ufb01i")); -} - - void testDiffHalfmatch() { - // Detect a halfmatch. - dmp.Diff_Timeout = 1; - assertEmpty("diff_halfMatch: No match #1.", diff_halfMatch("1234567890", "abcdef")); - - assertEmpty("diff_halfMatch: No match #2.", diff_halfMatch("12345", "23")); - - assertEquals("diff_halfMatch: Single Match #1.", split("12,90,a,z,345678", ','), diff_halfMatch("1234567890", "a345678z")); - - assertEquals("diff_halfMatch: Single Match #2.", split("a,z,12,90,345678", ','), diff_halfMatch("a345678z", "1234567890")); - - assertEquals("diff_halfMatch: Single Match #3.", split("abc,z,1234,0,56789", ','), diff_halfMatch("abc56789z", "1234567890")); - - assertEquals("diff_halfMatch: Single Match #4.", split("a,xyz,1,7890,23456", ','), diff_halfMatch("a23456xyz", "1234567890")); - - assertEquals("diff_halfMatch: Multiple Matches #1.", split("12123,123121,a,z,1234123451234", ','), diff_halfMatch("121231234123451234123121", "a1234123451234z")); - - assertEquals("diff_halfMatch: Multiple Matches #2.", split(",-=-=-=-=-=,x,,x-=-=-=-=-=-=-=", ','), diff_halfMatch("x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-=")); - - assertEquals("diff_halfMatch: Multiple Matches #3.", split("-=-=-=-=-=,,,y,-=-=-=-=-=-=-=y", ','), diff_halfMatch("-=-=-=-=-=-=-=-=-=-=-=-=y", "-=-=-=-=-=-=-=yy")); - - // Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy - assertEquals("diff_halfMatch: Non-optimal halfmatch.", split("qHillo,w,x,Hulloy,HelloHe", ','), diff_halfMatch("qHilloHelloHew", "xHelloHeHulloy")); - - // dmp.Diff_Timeout = 0; - // assertEmpty("diff_halfMatch: Optimal no halfmatch.", diff_halfMatch("qHilloHelloHew", "xHelloHeHulloy")); - } - - void testDiffLinesToChars() { - // Convert lines down to characters. - Lines tmpVector, resVector; - tmpVector.text1 = string_t("alpha\n"); - tmpVector.text2 = string_t("beta\n"); - tmpVector.resize(3); - tmpVector[1] = LinePtr(tmpVector.text1.c_str(), tmpVector.text1.length()); - tmpVector[2] = LinePtr(tmpVector.text2.c_str(), tmpVector.text2.length()); - string_t text1("alpha\nbeta\nalpha\n"), text2("beta\nalpha\nbeta\n"); - dmp.diff_linesToChars(text1, text2, resVector); - assertEquals("diff_linesToChars:", "\1\2\1", "\2\1\2", tmpVector, text1, text2, resVector); - - tmpVector.text1 = string_t("alpha\r\n"); - tmpVector.text2 = string_t("beta\r\n\r\n"); - tmpVector.resize(4); - tmpVector[1] = LinePtr(tmpVector.text1.c_str(), tmpVector.text1.length()); - tmpVector[2] = LinePtr(tmpVector.text2.c_str(), tmpVector.text2.length() - 2); - tmpVector[3] = LinePtr(tmpVector.text2.c_str() + 6, 2); - text1.clear(), text2 = "alpha\r\nbeta\r\n\r\n\r\n"; - dmp.diff_linesToChars(text1, text2, resVector); - assertEquals("diff_linesToChars:", string_t(), "\1\2\3\3", tmpVector, text1, text2, resVector); - - tmpVector.text1 = string_t("a"); - tmpVector.text2 = string_t("b"); - tmpVector.resize(3); - tmpVector[1] = LinePtr(tmpVector.text1.c_str(), tmpVector.text1.length()); - tmpVector[2] = LinePtr(tmpVector.text2.c_str(), tmpVector.text2.length()); - text1 = "a", text2 = "b"; - resVector.clear(); - dmp.diff_linesToChars(text1, text2, resVector); - assertEquals("diff_linesToChars:", "\1", "\2", tmpVector, text1, text2, resVector); - - // More than 256 to reveal any 8-bit limitations. - size_t n = 300; - tmpVector.resize(n + 1); - tmpVector[0].second = 0; - basic_stringstream lines; - string_t chars; - for (size_t x = 1; x < n + 1; x++) { - lines << x << traits::eol; - tmpVector[x].second = lines.str().size(); - chars += (wchar_t)x; - } - tmpVector.text1 = lines.str(); - for (size_t x = 1, prev = 0; x < n + 1; x++) { - tmpVector[x].first = tmpVector.text1.c_str() + prev; - tmpVector[x].second -= prev; - prev += tmpVector[x].second; - } - assertEquals("diff_linesToChars: More than 256 (setup).", n + 1, tmpVector.size()); - assertEquals("diff_linesToChars: More than 256 (setup).", n, chars.length()); - text1 = tmpVector.text1, text2.clear(); - resVector.clear(); - dmp.diff_linesToChars(text1, text2, resVector); - assertEquals("diff_linesToChars:", chars, "", tmpVector, text1, text2, resVector); - } - - void testDiffCharsToLines() { - // First check that Diff equality works. - assertTrue("diff_charsToLines:", Diff(EQUAL, "a") == Diff(EQUAL, "a")); - - assertEquals("diff_charsToLines:", Diff(EQUAL, "a"), Diff(EQUAL, "a")); - - // Convert chars up to lines. - Diffs diffs; - diffs.push_back(Diff(EQUAL, "\1\2\1")); // ("\u0001\u0002\u0001"); - diffs.push_back(Diff(INSERT, "\2\1\2")); // ("\u0002\u0001\u0002"); - Lines tmpVector; - tmpVector.text1 = string_t("alpha\n"); - tmpVector.text2 = string_t("beta\n"); - tmpVector.resize(3); - tmpVector[1] = LinePtr(tmpVector.text1.c_str(), tmpVector.text1.length()); - tmpVector[2] = LinePtr(tmpVector.text2.c_str(), tmpVector.text2.length()); - dmp.diff_charsToLines(diffs, tmpVector); - assertEquals("diff_charsToLines:", diffList(Diff(EQUAL, "alpha\nbeta\nalpha\n"), Diff(INSERT, "beta\nalpha\nbeta\n")), diffs); - - // More than 256 to reveal any 8-bit limitations. - size_t n = 300; - tmpVector.resize(n + 1); - tmpVector[0].second = 0; - basic_stringstream lines; - string_t chars; - for (size_t x = 1; x < n + 1; x++) { - lines << x << traits::eol; - tmpVector[x].second = lines.str().size(); - chars += (char_t)x; - } - tmpVector.text1 = lines.str(); - for (size_t x = 1, prev = 0; x < n + 1; x++) { - tmpVector[x].first = tmpVector.text1.c_str() + prev; - tmpVector[x].second -= prev; - prev += tmpVector[x].second; - } - assertEquals("diff_linesToChars: More than 256 (setup).", n + 1, tmpVector.size()); - assertEquals("diff_linesToChars: More than 256 (setup).", n, chars.length()); - diffs = diffList(Diff(DELETE, chars)); - dmp.diff_charsToLines(diffs, tmpVector); - assertEquals("diff_charsToLines: More than 256.", diffList(Diff(DELETE, tmpVector.text1)), diffs); - } - - void testDiffCleanupMerge() { - // Cleanup a messy diff. - Diffs diffs; - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Null case.", diffList(), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "b"), Diff(INSERT, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: No change case.", diffList(Diff(EQUAL, "a"), Diff(DELETE, "b"), Diff(INSERT, "c")), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(EQUAL, "b"), Diff(EQUAL, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Merge equalities.", diffList(Diff(EQUAL, "abc")), diffs); - - diffs = diffList(Diff(DELETE, "a"), Diff(DELETE, "b"), Diff(DELETE, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Merge deletions.", diffList(Diff(DELETE, "abc")), diffs); - - diffs = diffList(Diff(INSERT, "a"), Diff(INSERT, "b"), Diff(INSERT, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Merge insertions.", diffList(Diff(INSERT, "abc")), diffs); - - diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, "b"), Diff(DELETE, "c"), Diff(INSERT, "d"), Diff(EQUAL, "e"), Diff(EQUAL, "f")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Merge interweave.", diffList(Diff(DELETE, "ac"), Diff(INSERT, "bd"), Diff(EQUAL, "ef")), diffs); - - diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, "abc"), Diff(DELETE, "dc")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Prefix and suffix detection.", diffList(Diff(EQUAL, "a"), Diff(DELETE, "d"), Diff(INSERT, "b"), Diff(EQUAL, "c")), diffs); - - diffs = diffList(Diff(EQUAL, "x"), Diff(DELETE, "a"), Diff(INSERT, "abc"), Diff(DELETE, "dc"), Diff(EQUAL, "y")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Prefix and suffix detection with equalities.", diffList(Diff(EQUAL, "xa"), Diff(DELETE, "d"), Diff(INSERT, "b"), Diff(EQUAL, "cy")), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(INSERT, "ba"), Diff(EQUAL, "c")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Slide edit left.", diffList(Diff(INSERT, "ab"), Diff(EQUAL, "ac")), diffs); - - diffs = diffList(Diff(EQUAL, "c"), Diff(INSERT, "ab"), Diff(EQUAL, "a")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Slide edit right.", diffList(Diff(EQUAL, "ca"), Diff(INSERT, "ba")), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "b"), Diff(EQUAL, "c"), Diff(DELETE, "ac"), Diff(EQUAL, "x")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Slide edit left recursive.", diffList(Diff(DELETE, "abc"), Diff(EQUAL, "acx")), diffs); - - diffs = diffList(Diff(EQUAL, "x"), Diff(DELETE, "ca"), Diff(EQUAL, "c"), Diff(DELETE, "b"), Diff(EQUAL, "a")); - dmp.diff_cleanupMerge(diffs); - assertEquals("diff_cleanupMerge: Slide edit right recursive.", diffList(Diff(EQUAL, "xca"), Diff(DELETE, "cba")), diffs); - } - - void testDiffCleanupSemanticLossless() { - // Slide diffs to match logical boundaries. - Diffs diffs = diffList(); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Null case.", diffList(), diffs); - - diffs = diffList(Diff(EQUAL, "AAA\r\n\r\nBBB"), Diff(INSERT, "\r\nDDD\r\n\r\nBBB"), Diff(EQUAL, "\r\nEEE")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemanticLossless: Blank lines.", diffList(Diff(EQUAL, "AAA\r\n\r\n"), Diff(INSERT, "BBB\r\nDDD\r\n\r\n"), Diff(EQUAL, "BBB\r\nEEE")), diffs); - - diffs = diffList(Diff(EQUAL, "AAA\r\nBBB"), Diff(INSERT, " DDD\r\nBBB"), Diff(EQUAL, " EEE")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemanticLossless: Line boundaries.", diffList(Diff(EQUAL, "AAA\r\n"), Diff(INSERT, "BBB DDD\r\n"), Diff(EQUAL, "BBB EEE")), diffs); - - diffs = diffList(Diff(EQUAL, "The c"), Diff(INSERT, "ow and the c"), Diff(EQUAL, "at.")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Word boundaries.", diffList(Diff(EQUAL, "The "), Diff(INSERT, "cow and the "), Diff(EQUAL, "cat.")), diffs); - - diffs = diffList(Diff(EQUAL, "The-c"), Diff(INSERT, "ow-and-the-c"), Diff(EQUAL, "at.")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Alphanumeric boundaries.", diffList(Diff(EQUAL, "The-"), Diff(INSERT, "cow-and-the-"), Diff(EQUAL, "cat.")), diffs); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "a"), Diff(EQUAL, "ax")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Hitting the start.", diffList(Diff(DELETE, "a"), Diff(EQUAL, "aax")), diffs); - - diffs = diffList(Diff(EQUAL, "xa"), Diff(DELETE, "a"), Diff(EQUAL, "a")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Hitting the end.", diffList(Diff(EQUAL, "xaa"), Diff(DELETE, "a")), diffs); - - diffs = diffList(Diff(EQUAL, "The xxx. The "), Diff(INSERT, "zzz. The "), Diff(EQUAL, "yyy.")); - dmp.diff_cleanupSemanticLossless(diffs); - assertEquals("diff_cleanupSemantic: Sentence boundaries.", diffList(Diff(EQUAL, "The xxx."), Diff(INSERT, " The zzz."), Diff(EQUAL, " The yyy.")), diffs); -} - - void testDiffCleanupSemantic() { - // Cleanup semantically trivial equalities. - Diffs diffs = diffList(); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Null case.", diffList(), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "cd"), Diff(EQUAL, "12"), Diff(DELETE, "e")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: No elimination #1.", diffList(Diff(DELETE, "ab"), Diff(INSERT, "cd"), Diff(EQUAL, "12"), Diff(DELETE, "e")), diffs); - - diffs = diffList(Diff(DELETE, "abc"), Diff(INSERT, "ABC"), Diff(EQUAL, "1234"), Diff(DELETE, "wxyz")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: No elimination #2.", diffList(Diff(DELETE, "abc"), Diff(INSERT, "ABC"), Diff(EQUAL, "1234"), Diff(DELETE, "wxyz")), diffs); - - diffs = diffList(Diff(DELETE, "a"), Diff(EQUAL, "b"), Diff(DELETE, "c")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Simple elimination.", diffList(Diff(DELETE, "abc"), Diff(INSERT, "b")), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(EQUAL, "cd"), Diff(DELETE, "e"), Diff(EQUAL, "f"), Diff(INSERT, "g")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Backpass elimination.", diffList(Diff(DELETE, "abcdef"), Diff(INSERT, "cdfg")), diffs); - - diffs = diffList(Diff(INSERT, "1"), Diff(EQUAL, "A"), Diff(DELETE, "B"), Diff(INSERT, "2"), Diff(EQUAL, "_"), Diff(INSERT, "1"), Diff(EQUAL, "A"), Diff(DELETE, "B"), Diff(INSERT, "2")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Multiple elimination.", diffList(Diff(DELETE, "AB_AB"), Diff(INSERT, "1A2_1A2")), diffs); - - diffs = diffList(Diff(EQUAL, "The c"), Diff(DELETE, "ow and the c"), Diff(EQUAL, "at.")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Word boundaries.", diffList(Diff(EQUAL, "The "), Diff(DELETE, "cow and the "), Diff(EQUAL, "cat.")), diffs); - - diffs = diffList(Diff(DELETE, "abcxx"), Diff(INSERT, "xxdef")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: No overlap elimination.", diffList(Diff(DELETE, "abcxx"), Diff(INSERT, "xxdef")), diffs); - - diffs = diffList(Diff(DELETE, "abcxxx"), Diff(INSERT, "xxxdef")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Overlap elimination.", diffList(Diff(DELETE, "abc"), Diff(EQUAL, "xxx"), Diff(INSERT, "def")), diffs); - - diffs = diffList(Diff(DELETE, "xxxabc"), Diff(INSERT, "defxxx")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Reverse overlap elimination.", diffList(Diff(INSERT, "def"), Diff(EQUAL, "xxx"), Diff(DELETE, "abc")), diffs); - - diffs = diffList(Diff(DELETE, "abcd1212"), Diff(INSERT, "1212efghi"), Diff(EQUAL, "----"), Diff(DELETE, "A3"), Diff(INSERT, "3BC")); - dmp.diff_cleanupSemantic(diffs); - assertEquals("diff_cleanupSemantic: Two overlap eliminations.", diffList(Diff(DELETE, "abcd"), Diff(EQUAL, "1212"), Diff(INSERT, "efghi"), Diff(EQUAL, "----"), Diff(DELETE, "A"), Diff(EQUAL, "3"), Diff(INSERT, "BC")), diffs); - } - - void testDiffCleanupEfficiency() { - // Cleanup operationally trivial equalities. - dmp.Diff_EditCost = 4; - Diffs diffs = diffList(); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: Null case.", diffList(), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "wxyz"), Diff(DELETE, "cd"), Diff(INSERT, "34")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: No elimination.", diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "wxyz"), Diff(DELETE, "cd"), Diff(INSERT, "34")), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "xyz"), Diff(DELETE, "cd"), Diff(INSERT, "34")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: Four-edit elimination.", diffList(Diff(DELETE, "abxyzcd"), Diff(INSERT, "12xyz34")), diffs); - - diffs = diffList(Diff(INSERT, "12"), Diff(EQUAL, "x"), Diff(DELETE, "cd"), Diff(INSERT, "34")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: Three-edit elimination.", diffList(Diff(DELETE, "xcd"), Diff(INSERT, "12x34")), diffs); - - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "xy"), Diff(INSERT, "34"), Diff(EQUAL, "z"), Diff(DELETE, "cd"), Diff(INSERT, "56")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: Backpass elimination.", diffList(Diff(DELETE, "abxyzcd"), Diff(INSERT, "12xy34z56")), diffs); - - dmp.Diff_EditCost = 5; - diffs = diffList(Diff(DELETE, "ab"), Diff(INSERT, "12"), Diff(EQUAL, "wxyz"), Diff(DELETE, "cd"), Diff(INSERT, "34")); - dmp.diff_cleanupEfficiency(diffs); - assertEquals("diff_cleanupEfficiency: High cost elimination.", diffList(Diff(DELETE, "abwxyzcd"), Diff(INSERT, "12wxyz34")), diffs); - dmp.Diff_EditCost = 4; - } - - void testDiffPrettyHtml() { - // Pretty print. - Diffs diffs = diffList(Diff(EQUAL, "a\n"), Diff(DELETE, "b"), Diff(INSERT, "c&d")); - assertEquals("diff_prettyHtml:", "
<B>b</B>c&d", dmp.diff_prettyHtml(diffs)); - } - - void testDiffText() { - // Compute the source and destination texts. - Diffs diffs = diffList(Diff(EQUAL, "jump"), Diff(DELETE, "s"), Diff(INSERT, "ed"), Diff(EQUAL, " over "), Diff(DELETE, "the"), Diff(INSERT, "a"), Diff(EQUAL, " lazy")); - assertEquals("diff_text1:", "jumps over the lazy", dmp.diff_text1(diffs)); - assertEquals("diff_text2:", "jumped over a lazy", dmp.diff_text2(diffs)); - } - - void testDiffDelta() { - // Convert a diff into delta string. - Diffs diffs = diffList(Diff(EQUAL, "jump"), Diff(DELETE, "s"), Diff(INSERT, "ed"), Diff(EQUAL, " over "), Diff(DELETE, "the"), Diff(INSERT, "a"), Diff(EQUAL, " lazy"), Diff(INSERT, "old dog")); - string_t text1 = dmp.diff_text1(diffs); - assertEquals("diff_text1: Base text.", "jumps over the lazy", text1); - - string_t delta = dmp.diff_toDelta(diffs); - assertEquals("diff_toDelta:", "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", delta); - - // Convert delta string into a diff. - assertEquals("diff_fromDelta: Normal.", diffs, dmp.diff_fromDelta(text1, delta)); - - // Generates error (19 < 20). - try { - dmp.diff_fromDelta(text1 + "x", delta); - assertFalse("diff_fromDelta: Too long.", true); - } catch (string_t ex) { - // Exception expected. - } - - // Generates error (19 > 18). - try { - dmp.diff_fromDelta(text1.substr(1), delta); - assertFalse("diff_fromDelta: Too short.", true); - } catch (string_t ex) { - // Exception expected. - } - - // Generates error (%c3%xy invalid Unicode). - try { - dmp.diff_fromDelta("", "+%c3%xy"); - assertFalse("diff_fromDelta: Invalid character.", true); - } catch (string_t ex) { - // Exception expected. - } - - // Test deltas with special characters. - diffs = diffList(Diff(EQUAL, string_t(L"\u0680 \000 \t %", 7)), Diff(DELETE, string_t(L"\u0681 \001 \n ^", 7)), Diff(INSERT, string_t(L"\u0682 \002 \\ |", 7))); - text1 = dmp.diff_text1(diffs); - assertEquals("diff_text1: Unicode text.", string_t(L"\u0680 \000 \t %\u0681 \001 \n ^", 14), text1); - - delta = dmp.diff_toDelta(diffs); - assertEquals("diff_toDelta: Unicode.", "=7\t-7\t+%DA%82 %02 %5C %7C", delta); - - assertEquals("diff_fromDelta: Unicode.", diffs, dmp.diff_fromDelta(text1, delta)); - - // Verify pool of unchanged characters. - diffs = diffList(Diff(INSERT, "A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # ")); - string_t text2 = dmp.diff_text2(diffs); - assertEquals("diff_text2: Unchanged characters.", "A-Z a-z 0-9 - _ . ! ~ * \' ( ) ; / ? : @ & = + $ , # ", text2); - - delta = dmp.diff_toDelta(diffs); - assertEquals("diff_toDelta: Unchanged characters.", "+A-Z a-z 0-9 - _ . ! ~ * \' ( ) ; / ? : @ & = + $ , # ", delta); - - // Convert delta string into a diff. - assertEquals("diff_fromDelta: Unchanged characters.", diffs, dmp.diff_fromDelta("", delta)); - } - - void testDiffXIndex() { - // Translate a location in text1 to text2. - Diffs diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, "1234"), Diff(EQUAL, "xyz")); - assertEquals("diff_xIndex: Translation on equality.", 5, dmp.diff_xIndex(diffs, 2)); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "1234"), Diff(EQUAL, "xyz")); - assertEquals("diff_xIndex: Translation on deletion.", 1, dmp.diff_xIndex(diffs, 3)); - } - - void testDiffLevenshtein() { - Diffs diffs = diffList(Diff(DELETE, "abc"), Diff(INSERT, "1234"), Diff(EQUAL, "xyz")); - assertEquals("diff_levenshtein: Trailing equality.", 4, dmp.diff_levenshtein(diffs)); - - diffs = diffList(Diff(EQUAL, "xyz"), Diff(DELETE, "abc"), Diff(INSERT, "1234")); - assertEquals("diff_levenshtein: Leading equality.", 4, dmp.diff_levenshtein(diffs)); - - diffs = diffList(Diff(DELETE, "abc"), Diff(EQUAL, "xyz"), Diff(INSERT, "1234")); - assertEquals("diff_levenshtein: Middle equality.", 7, dmp.diff_levenshtein(diffs)); - } - - void testDiffBisect() { - // Normal. - string_t a = "cat"; - string_t b = "map"; - // Since the resulting diff hasn't been normalized, it would be ok if - // the insertion and deletion pairs are swapped. - // If the order changes, tweak this test as required. - Diffs diffs = diffList(Diff(DELETE, "c"), Diff(INSERT, "m"), Diff(EQUAL, "a"), Diff(DELETE, "t"), Diff(INSERT, "p")); - assertEquals("diff_bisect: Normal.", diffs, dmp.diff_bisect(a, b, numeric_limits::max())); - - // Timeout. - while (clock() == 0); // Wait for the first tick - diffs = diffList(Diff(DELETE, "cat"), Diff(INSERT, "map")); - assertEquals("diff_bisect: Timeout.", diffs, dmp.diff_bisect(a, b, 0)); - } - - void testDiffMain() { - // Perform a trivial diff. - Diffs diffs = diffList(); - assertEquals("diff_main: Null case.", diffs, dmp.diff_main("", "", false)); - - diffs = diffList(Diff(EQUAL, "abc")); - assertEquals("diff_main: Equality.", diffs, dmp.diff_main("abc", "abc", false)); - - diffs = diffList(Diff(EQUAL, "ab"), Diff(INSERT, "123"), Diff(EQUAL, "c")); - assertEquals("diff_main: Simple insertion.", diffs, dmp.diff_main("abc", "ab123c", false)); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "123"), Diff(EQUAL, "bc")); - assertEquals("diff_main: Simple deletion.", diffs, dmp.diff_main("a123bc", "abc", false)); - - diffs = diffList(Diff(EQUAL, "a"), Diff(INSERT, "123"), Diff(EQUAL, "b"), Diff(INSERT, "456"), Diff(EQUAL, "c")); - assertEquals("diff_main: Two insertions.", diffs, dmp.diff_main("abc", "a123b456c", false)); - - diffs = diffList(Diff(EQUAL, "a"), Diff(DELETE, "123"), Diff(EQUAL, "b"), Diff(DELETE, "456"), Diff(EQUAL, "c")); - assertEquals("diff_main: Two deletions.", diffs, dmp.diff_main("a123b456c", "abc", false)); - - // Perform a real diff. - // Switch off the timeout. - dmp.Diff_Timeout = 0; - diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, "b")); - assertEquals("diff_main: Simple case #1.", diffs, dmp.diff_main("a", "b", false)); - - diffs = diffList(Diff(DELETE, "Apple"), Diff(INSERT, "Banana"), Diff(EQUAL, "s are a"), Diff(INSERT, "lso"), Diff(EQUAL, " fruit.")); - assertEquals("diff_main: Simple case #2.", diffs, dmp.diff_main("Apples are a fruit.", "Bananas are also fruit.", false)); - - diffs = diffList(Diff(DELETE, "a"), Diff(INSERT, L"\u0680"), Diff(EQUAL, "x"), Diff(DELETE, "\t"), Diff(INSERT, string_t("\000", 1))); - assertEquals("diff_main: Simple case #3.", diffs, dmp.diff_main("ax\t", string_t(L"\u0680x\000", 3), false)); - - diffs = diffList(Diff(DELETE, "1"), Diff(EQUAL, "a"), Diff(DELETE, "y"), Diff(EQUAL, "b"), Diff(DELETE, "2"), Diff(INSERT, "xab")); - assertEquals("diff_main: Overlap #1.", diffs, dmp.diff_main("1ayb2", "abxab", false)); - - diffs = diffList(Diff(INSERT, "xaxcx"), Diff(EQUAL, "abc"), Diff(DELETE, "y")); - assertEquals("diff_main: Overlap #2.", diffs, dmp.diff_main("abcy", "xaxcxabc", false)); - - diffs = diffList(Diff(DELETE, "ABCD"), Diff(EQUAL, "a"), Diff(DELETE, "="), Diff(INSERT, "-"), Diff(EQUAL, "bcd"), Diff(DELETE, "="), Diff(INSERT, "-"), Diff(EQUAL, "efghijklmnopqrs"), Diff(DELETE, "EFGHIJKLMNOefg")); - assertEquals("diff_main: Overlap #3.", diffs, dmp.diff_main("ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", "a-bcd-efghijklmnopqrs", false)); - - diffs = diffList(Diff(INSERT, " "), Diff(EQUAL, "a"), Diff(INSERT, "nd"), Diff(EQUAL, " [[Pennsylvania]]"), Diff(DELETE, " and [[New")); - assertEquals("diff_main: Large equality.", diffs, dmp.diff_main("a [[Pennsylvania]] and [[New", " and [[Pennsylvania]]", false)); - - dmp.Diff_Timeout = 0.1f; // 100ms - // This test may 'fail' on extremely fast computers. If so, just increase the text lengths. - string_t a = "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n"; - string_t b = "I am the very model of a modern major general,\nI've information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n"; - // Increase the text lengths by 1024 times to ensure a timeout. - for (int x = 0; x < 10; x++) { - a = a + a; - b = b + b; - } - clock_t startTime = clock(); - dmp.diff_main(a, b); - clock_t endTime = clock(); - // Test that we took at least the timeout period. - assertTrue("diff_main: Timeout min.", dmp.Diff_Timeout * CLOCKS_PER_SEC <= endTime - startTime); - // Test that we didn't take forever (be forgiving). - // Theoretically this test could fail very occasionally if the - // OS task swaps or locks up for a second at the wrong moment. - // Java seems to overrun by ~80% (compared with 10% for other languages). - // Therefore use an upper limit of 0.5s instead of 0.2s. - assertTrue("diff_main: Timeout max.", dmp.Diff_Timeout * CLOCKS_PER_SEC * 2 > endTime - startTime); - dmp.Diff_Timeout = 0; - - // Test the linemode speedup. - // Must be long to pass the 100 char cutoff. - a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n"; - b = "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n"; - assertEquals("diff_main: Simple line-mode.", dmp.diff_main(a, b, true), dmp.diff_main(a, b, false)); - - a = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; - b = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"; - assertEquals("diff_main: Single line-mode.", dmp.diff_main(a, b, true), dmp.diff_main(a, b, false)); - - a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n"; - b = "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n"; - Strings texts_linemode = diff_rebuildtexts(dmp.diff_main(a, b, true)); - Strings texts_textmode = diff_rebuildtexts(dmp.diff_main(a, b, false)); - assertEquals("diff_main: Overlap line-mode.", texts_textmode, texts_linemode); - - // Test null inputs -- not needed because nulls can't be passed in string_t&. - } - - - // MATCH TEST FUNCTIONS - - void testMatchAlphabet() { - // Initialise the bitmasks for Bitap. - map bitmask, bitmask2; - bitmask['a'] = 4; - bitmask['b'] = 2; - bitmask['c'] = 1; - dmp.match_alphabet("abc", bitmask2); - assertEquals("match_alphabet: Unique.", bitmask, bitmask2); - - bitmask.clear(); - bitmask['a'] = 37; - bitmask['b'] = 18; - bitmask['c'] = 8; - bitmask2.clear(); - dmp.match_alphabet("abcaba", bitmask2); - assertEquals("match_alphabet: Duplicates.", bitmask, bitmask2); - } - - void testMatchBitap() { - // Bitap algorithm. - dmp.Match_Distance = 100; - dmp.Match_Threshold = 0.5f; - assertEquals("match_bitap: Exact match #1.", 5, dmp.match_bitap("abcdefghijk", "fgh", 5)); - - assertEquals("match_bitap: Exact match #2.", 5, dmp.match_bitap("abcdefghijk", "fgh", 0)); - - assertEquals("match_bitap: Fuzzy match #1.", 4, dmp.match_bitap("abcdefghijk", "efxhi", 0)); - - assertEquals("match_bitap: Fuzzy match #2.", 2, dmp.match_bitap("abcdefghijk", "cdefxyhijk", 5)); - - assertEquals("match_bitap: Fuzzy match #3.", -1, dmp.match_bitap("abcdefghijk", "bxy", 1)); - - assertEquals("match_bitap: Overflow.", 2, dmp.match_bitap("123456789xx0", "3456789x0", 2)); - - assertEquals("match_bitap: Before start match.", 0, dmp.match_bitap("abcdef", "xxabc", 4)); - - assertEquals("match_bitap: Beyond end match.", 3, dmp.match_bitap("abcdef", "defyy", 4)); - - assertEquals("match_bitap: Oversized pattern.", 0, dmp.match_bitap("abcdef", "xabcdefy", 0)); - - dmp.Match_Threshold = 0.4f; - assertEquals("match_bitap: Threshold #1.", 4, dmp.match_bitap("abcdefghijk", "efxyhi", 1)); - - dmp.Match_Threshold = 0.3f; - assertEquals("match_bitap: Threshold #2.", -1, dmp.match_bitap("abcdefghijk", "efxyhi", 1)); - - dmp.Match_Threshold = 0.0f; - assertEquals("match_bitap: Threshold #3.", 1, dmp.match_bitap("abcdefghijk", "bcdef", 1)); - - dmp.Match_Threshold = 0.5f; - assertEquals("match_bitap: Multiple select #1.", 0, dmp.match_bitap("abcdexyzabcde", "abccde", 3)); - - assertEquals("match_bitap: Multiple select #2.", 8, dmp.match_bitap("abcdexyzabcde", "abccde", 5)); - - dmp.Match_Distance = 10; // Strict location. - assertEquals("match_bitap: Distance test #1.", -1, dmp.match_bitap("abcdefghijklmnopqrstuvwxyz", "abcdefg", 24)); - - assertEquals("match_bitap: Distance test #2.", 0, dmp.match_bitap("abcdefghijklmnopqrstuvwxyz", "abcdxxefg", 1)); - - dmp.Match_Distance = 1000; // Loose location. - assertEquals("match_bitap: Distance test #3.", 0, dmp.match_bitap("abcdefghijklmnopqrstuvwxyz", "abcdefg", 24)); - } - - void testMatchMain() { - // Full match. - assertEquals("match_main: Equality.", 0, dmp.match_main("abcdef", "abcdef", 1000)); - - assertEquals("match_main: Null text.", -1, dmp.match_main("", "abcdef", 1)); - - assertEquals("match_main: Null pattern.", 3, dmp.match_main("abcdef", "", 3)); - - assertEquals("match_main: Exact match.", 3, dmp.match_main("abcdef", "de", 3)); - - dmp.Match_Threshold = 0.7f; - assertEquals("match_main: Complex match.", 4, dmp.match_main("I am the very model of a modern major general.", " that berry ", 5)); - dmp.Match_Threshold = 0.5f; - - // Test null inputs -- not needed because nulls can't be passed in string_t&. - } - - - // PATCH TEST FUNCTIONS - - void testPatchObj() { - // Patch Object. - Patch p; - p.start1 = 20; - p.start2 = 21; - p.length1 = 18; - p.length2 = 17; - p.diffs = diffList(Diff(EQUAL, "jump"), Diff(DELETE, "s"), Diff(INSERT, "ed"), Diff(EQUAL, " over "), Diff(DELETE, "the"), Diff(INSERT, "a"), Diff(EQUAL, "\nlaz")); - string_t strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n"; - assertEquals("Patch: toString.", strp, p.toString()); - } - - void testPatchFromText() { - assertTrue("patch_fromText: #0.", dmp.patch_fromText("").empty()); - - string_t strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n"; - assertEquals("patch_fromText: #1.", strp, dmp.patch_fromText(strp).front().toString()); - - assertEquals("patch_fromText: #2.", "@@ -1 +1 @@\n-a\n+b\n", dmp.patch_fromText("@@ -1 +1 @@\n-a\n+b\n").front().toString()); - - assertEquals("patch_fromText: #3.", "@@ -1,3 +0,0 @@\n-abc\n", dmp.patch_fromText("@@ -1,3 +0,0 @@\n-abc\n").front().toString()); - - assertEquals("patch_fromText: #4.", "@@ -0,0 +1,3 @@\n+abc\n", dmp.patch_fromText("@@ -0,0 +1,3 @@\n+abc\n").front().toString()); - - // Generates error. - try { - dmp.patch_fromText("Bad\nPatch\n"); - assertFalse("patch_fromText: #5.", true); - } catch (string_t ex) { - // Exception expected. - } - } - - void testPatchToText() { - string_t strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"; - Patches patches; - patches = dmp.patch_fromText(strp); - assertEquals("patch_toText: Single", strp, dmp.patch_toText(patches)); - - strp = "@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n"; - patches = dmp.patch_fromText(strp); - assertEquals("patch_toText: Dua", strp, dmp.patch_toText(patches)); - } - - void testPatchAddContext() { - dmp.Patch_Margin = 4; - Patch p; - p = dmp.patch_fromText("@@ -21,4 +21,10 @@\n-jump\n+somersault\n").front(); - dmp.patch_addContext(p, "The quick brown fox jumps over the lazy dog."); - assertEquals("patch_addContext: Simple case.", "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n", p.toString()); - - p = dmp.patch_fromText("@@ -21,4 +21,10 @@\n-jump\n+somersault\n").front(); - dmp.patch_addContext(p, "The quick brown fox jumps."); - assertEquals("patch_addContext: Not enough trailing context.", "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n", p.toString()); - - p = dmp.patch_fromText("@@ -3 +3,2 @@\n-e\n+at\n").front(); - dmp.patch_addContext(p, "The quick brown fox jumps."); - assertEquals("patch_addContext: Not enough leading context.", "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n qui\n", p.toString()); - - p = dmp.patch_fromText("@@ -3 +3,2 @@\n-e\n+at\n").front(); - dmp.patch_addContext(p, "The quick brown fox jumps. The quick brown fox crashes."); - assertEquals("patch_addContext: Ambiguity.", "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n quick brown fox jumps. \n", p.toString()); - } - - void testPatchMake() { - Patches patches; - patches = dmp.patch_make("", ""); - assertEquals("patch_make: Null case", "", dmp.patch_toText(patches)); - - string_t text1 = "The quick brown fox jumps over the lazy dog."; - string_t text2 = "That quick brown fox jumped over a lazy dog."; - string_t expectedPatch = "@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n"; - // The second patch must be "-21,17 +21,18", not "-22,17 +21,18" due to rolling context. - patches = dmp.patch_make(text2, text1); - assertEquals("patch_make: Text2+Text1 inputs", expectedPatch, dmp.patch_toText(patches)); - - expectedPatch = "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"; - patches = dmp.patch_make(text1, text2); - assertEquals("patch_make: Text1+Text2 inputs", expectedPatch, dmp.patch_toText(patches)); - - Diffs diffs = dmp.diff_main(text1, text2, false); - patches = dmp.patch_make(diffs); - assertEquals("patch_make: Diff input", expectedPatch, dmp.patch_toText(patches)); - - patches = dmp.patch_make(text1, diffs); - assertEquals("patch_make: Text1+Diff inputs", expectedPatch, dmp.patch_toText(patches)); - - patches = dmp.patch_make(text1, text2, diffs); - assertEquals("patch_make: Text1+Text2+Diff inputs (deprecated)", expectedPatch, dmp.patch_toText(patches)); - - patches = dmp.patch_make("`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?"); - assertEquals("patch_toText: Character encoding.", "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n", dmp.patch_toText(patches)); - - diffs = diffList(Diff(DELETE, "`1234567890-=[]\\;',./"), Diff(INSERT, "~!@#$%^&*()_+{}|:\"<>?")); - assertEquals("patch_fromText: Character decoding.", diffs, dmp.patch_fromText("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n").front().diffs); - - text1.clear(); - for (int x = 0; x < 100; x++) { - text1 += "abcdef"; - } - text2 = text1 + "123"; - expectedPatch = "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n"; - patches = dmp.patch_make(text1, text2); - assertEquals("patch_make: Long string with repeats.", expectedPatch, dmp.patch_toText(patches)); - - // Test null inputs -- not needed because nulls can't be passed in string_t&. - } - - void testPatchSplitMax() { - // Assumes that Match_MaxBits is 32. - Patches patches; - patches = dmp.patch_make("abcdefghijklmnopqrstuvwxyz01234567890", "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0"); - dmp.patch_splitMax(patches); - assertEquals("patch_splitMax: #1.", "@@ -1,32 +1,46 @@\n+X\n ab\n+X\n cd\n+X\n ef\n+X\n gh\n+X\n ij\n+X\n kl\n+X\n mn\n+X\n op\n+X\n qr\n+X\n st\n+X\n uv\n+X\n wx\n+X\n yz\n+X\n 012345\n@@ -25,13 +39,18 @@\n zX01\n+X\n 23\n+X\n 45\n+X\n 67\n+X\n 89\n+X\n 0\n", dmp.patch_toText(patches)); - - patches = dmp.patch_make("abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", "abcdefuvwxyz"); - string_t oldToText = dmp.patch_toText(patches); - dmp.patch_splitMax(patches); - assertEquals("patch_splitMax: #2.", oldToText, dmp.patch_toText(patches)); - - patches = dmp.patch_make("1234567890123456789012345678901234567890123456789012345678901234567890", "abc"); - dmp.patch_splitMax(patches); - assertEquals("patch_splitMax: #3.", "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n@@ -29,32 +1,4 @@\n-9012345678901234567890123456\n 7890\n@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n", dmp.patch_toText(patches)); - - patches = dmp.patch_make("abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1", "abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1"); - dmp.patch_splitMax(patches); - assertEquals("patch_splitMax: #4.", "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n@@ -29,32 +29,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n", dmp.patch_toText(patches)); - } - - void testPatchAddPadding() { - Patches patches; - patches = dmp.patch_make("", "test"); - assertEquals("patch_addPadding: Both edges full.", "@@ -0,0 +1,4 @@\n+test\n", dmp.patch_toText(patches)); - dmp.patch_addPadding(patches); - assertEquals("patch_addPadding: Both edges full.", "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n", dmp.patch_toText(patches)); - - patches = dmp.patch_make("XY", "XtestY"); - assertEquals("patch_addPadding: Both edges partial.", "@@ -1,2 +1,6 @@\n X\n+test\n Y\n", dmp.patch_toText(patches)); - dmp.patch_addPadding(patches); - assertEquals("patch_addPadding: Both edges partial.", "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n", dmp.patch_toText(patches)); - - patches = dmp.patch_make("XXXXYYYY", "XXXXtestYYYY"); - assertEquals("patch_addPadding: Both edges none.", "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n", dmp.patch_toText(patches)); - dmp.patch_addPadding(patches); - assertEquals("patch_addPadding: Both edges none.", "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n", dmp.patch_toText(patches)); - } - - void testPatchApply() { - dmp.Match_Distance = 1000; - dmp.Match_Threshold = 0.5f; - dmp.Patch_DeleteThreshold = 0.5f; - Patches patches; - patches = dmp.patch_make("", ""); - pair > results = dmp.patch_apply(patches, "Hello world."); - vector boolArray = results.second; - - basic_stringstream result; - result << results.first << traits::tab << boolArray.size(); - assertEquals("patch_apply: Null case.", "Hello world.\t0", result.str()); - - string_t resultStr; - patches = dmp.patch_make("The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog."); - results = dmp.patch_apply(patches, "The quick brown fox jumps over the lazy dog."); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Exact match.", "That quick brown fox jumped over a lazy dog.\ttrue\ttrue", resultStr); - - results = dmp.patch_apply(patches, "The quick red rabbit jumps over the tired tiger."); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Partial match.", "That quick red rabbit jumped over a tired tiger.\ttrue\ttrue", resultStr); - - results = dmp.patch_apply(patches, "I am the very model of a modern major general."); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Failed match.", "I am the very model of a modern major general.\tfalse\tfalse", resultStr); - - patches = dmp.patch_make("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy"); - results = dmp.patch_apply(patches, "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Big delete, small change.", "xabcy\ttrue\ttrue", resultStr); - - patches = dmp.patch_make("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy"); - results = dmp.patch_apply(patches, "x12345678901234567890---------------++++++++++---------------12345678901234567890y"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Big delete, large change 1.", "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y\tfalse\ttrue", resultStr); - - dmp.Patch_DeleteThreshold = 0.6f; - patches = dmp.patch_make("x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy"); - results = dmp.patch_apply(patches, "x12345678901234567890---------------++++++++++---------------12345678901234567890y"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Big delete, large change 2.", "xabcy\ttrue\ttrue", resultStr); - dmp.Patch_DeleteThreshold = 0.5f; - - dmp.Match_Threshold = 0.0f; - dmp.Match_Distance = 0; - patches = dmp.patch_make("abcdefghijklmnopqrstuvwxyz--------------------1234567890", "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890"); - results = dmp.patch_apply(patches, "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false") + "\t" + (boolArray[1] ? "true" : "false"); - assertEquals("patch_apply: Compensate for failed patch.", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890\tfalse\ttrue", resultStr); - dmp.Match_Threshold = 0.5f; - dmp.Match_Distance = 1000; - - patches = dmp.patch_make("", "test"); - string_t patchStr = dmp.patch_toText(patches); - dmp.patch_apply(patches, ""); - assertEquals("patch_apply: No side effects.", patchStr, dmp.patch_toText(patches)); - - patches = dmp.patch_make("The quick brown fox jumps over the lazy dog.", "Woof"); - patchStr = dmp.patch_toText(patches); - dmp.patch_apply(patches, "The quick brown fox jumps over the lazy dog."); - assertEquals("patch_apply: No side effects with major delete.", patchStr, dmp.patch_toText(patches)); - - patches = dmp.patch_make("", "test"); - results = dmp.patch_apply(patches, ""); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false"); - assertEquals("patch_apply: Edge exact match.", "test\ttrue", resultStr); - - patches = dmp.patch_make("XY", "XtestY"); - results = dmp.patch_apply(patches, "XY"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false"); - assertEquals("patch_apply: Near edge exact match.", "XtestY\ttrue", resultStr); - - patches = dmp.patch_make("y", "y123"); - results = dmp.patch_apply(patches, "x"); - boolArray = results.second; - resultStr = results.first + "\t" + (boolArray[0] ? "true" : "false"); - assertEquals("patch_apply: Edge partial match.", "x123\ttrue", resultStr); - } - - private: - // Define equality. - void assertEquals(const char* strCase, int n1, int n2) { - if (n1 != n2) { - cerr << strCase << " FAIL\nExpected: " << n1 << "\nActual: " << n2 << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const string_t &s1, const string_t &s2) { - if (s1 != s2) { - cerr << strCase << " FAIL\nExpected: " << s1 << "\nActual: " << s2 << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const Diff &d1, const Diff &d2) { - if (d1 != d2) { - cerr << strCase << " FAIL\nExpected: " << d1.toString() << "\nActual: " << d2.toString() << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const Diffs &list1, const Diffs &list2) { - bool fail = false; - if (list1.size() == list2.size()) { - for (Diffs::const_iterator d1 = list1.begin(), d2 = list2.begin(); d1 != list1.end(); ++d1, ++d2) { - if (*d1 != *d2) { - fail = true; - break; - } - } - } else { - fail = true; - } - - if (fail) { - // Build human readable description of both lists. - string_t listString1 = "("; - bool first = true; - for (Diffs::const_iterator d1 = list1.begin(); d1 != list1.end(); ++d1) { - if (!first) { - listString1 += ", "; - } - listString1 += (*d1).toString(); - first = false; - } - listString1 += ")"; - string_t listString2 = "("; - first = true; - for (Diffs::const_iterator d2 = list2.begin(); d2 != list2.end(); ++d2) { - if (!first) { - listString2 += ", "; - } - listString2 += (*d2).toString(); - first = false; - } - listString2 += ")"; - cerr << strCase << " FAIL\nExpected: " << listString1 << "\nActual: " << listString2 << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const string_t& text1_1, const string_t& text2_1, const Lines &lines1, - const string_t& text1_2, const string_t& text2_2, const Lines &lines2) - { - bool fail = false; - if (text1_1 != text1_2 || text2_1 != text2_2 || lines1.size() != lines2.size()) - fail = true; - else - for (Lines::const_iterator i = lines1.begin(), j = lines2.begin(); i != lines1.end(); ++i, ++j) - if (string_t((*i).first, (*i).second) != string_t((*j).first, (*j).second)) { fail = true; break; } - - if (fail) { - // Build human readable description of both lists. - string_t listString1 = "\"" + text1_1 + "\", \"" + text2_1 + "\", (\""; - bool first = true; - for (Lines::const_iterator i = lines1.begin() + 1; i != lines1.end(); ++i) { - if (!first) listString1 += "\", \""; - listString1.append((*i).first, (*i).second); - first = false; - } - listString1 += "\")"; - string_t listString2 = "\"" + text1_2 + "\", \"" + text2_2 + "\", (\""; - first = true; - for (Lines::const_iterator j = lines2.begin() + 1; j != lines2.end(); ++j) { - if (!first) listString2 += "\", \""; - listString2.append((*j).first, (*j).second); - first = false; - } - listString2 += "\")"; - cerr << strCase << " FAIL\nExpected: " << listString1 << "\nActual: " << listString2 << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const map &m1, const map &m2) { - map::const_iterator i1 = m1.begin(), i2 = m2.begin(); - - while (i1 != m1.end() && i2 != m2.end()) { - if ((*i1).first != (*i2).first || (*i1).second != (*i2).second) { - cerr << strCase << " FAIL\nExpected: (" << char((*i1).first) << ", " << (*i1).second << - ")\nActual: (" << char((*i2).first) << ", " << (*i2).second << ')' << endl; - throw strCase; - } - ++i1, ++i2; - } - - if (i1 != m1.end()) { - cerr << strCase << " FAIL\nExpected: (" << char((*i1).first) << ", " << (*i1).second << ")\nActual: none" << endl; - throw strCase; - } - if (i2 != m2.end()) { - cerr << strCase << " FAIL\nExpected: none\nActual: (" << char((*i2).first) << ", " << (*i2).second << ')' << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEquals(const char* strCase, const Strings &list1, const Strings &list2) { - if (list1 != list2) { - cerr << strCase << " FAIL\nExpected: " << join(list1, ',') << "\nActual: " << join(list2, ',') << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertTrue(const char* strCase, bool value) { - if (!value) { - cerr << strCase << " FAIL\nExpected: true\nActual: false" << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertFalse(const char* strCase, bool value) { - if (value) { - cerr << strCase << " FAIL\nExpected: false\nActual: true" << endl; - throw strCase; - } - cout << strCase << " OK" << endl; - } - - void assertEmpty(const char* strCase, const Strings &list) { - if (!list.empty()) { - throw strCase; - } - } - - // Construct the two texts which made up the diff originally. - static Strings diff_rebuildtexts(Diffs diffs) { - Strings text; - text.push_back(string_t()); - text.push_back(string_t()); - Strings::iterator t1 = text.begin(), t0 = t1++; - for (Diffs::const_iterator myDiff = diffs.begin(); myDiff != diffs.end(); ++myDiff) { - if ((*myDiff).operation != INSERT) { - *t0 += (*myDiff).text; - } - if ((*myDiff).operation != DELETE) { - *t1 += (*myDiff).text; - } - } - return text; - } - - // Private function for quickly building lists of diffs. - static Diffs diffList( - // Diff(INSERT, NULL) is invalid and thus is used as the default argument. - Diff d1 = Diff(INSERT, string_t()), Diff d2 = Diff(INSERT, string_t()), - Diff d3 = Diff(INSERT, string_t()), Diff d4 = Diff(INSERT, string_t()), - Diff d5 = Diff(INSERT, string_t()), Diff d6 = Diff(INSERT, string_t()), - Diff d7 = Diff(INSERT, string_t()), Diff d8 = Diff(INSERT, string_t()), - Diff d9 = Diff(INSERT, string_t()), Diff d10 = Diff(INSERT, string_t())) { - // Diff(INSERT, NULL) is invalid and thus is used as the default argument. - Diffs listRet; - if (d1.operation == INSERT && d1.text.empty()) { - return listRet; - } - listRet.push_back(d1); - - if (d2.operation == INSERT && d2.text.empty()) { - return listRet; - } - listRet.push_back(d2); - - if (d3.operation == INSERT && d3.text.empty()) { - return listRet; - } - listRet.push_back(d3); - - if (d4.operation == INSERT && d4.text.empty()) { - return listRet; - } - listRet.push_back(d4); - - if (d5.operation == INSERT && d5.text.empty()) { - return listRet; - } - listRet.push_back(d5); - - if (d6.operation == INSERT && d6.text.empty()) { - return listRet; - } - listRet.push_back(d6); - - if (d7.operation == INSERT && d7.text.empty()) { - return listRet; - } - listRet.push_back(d7); - - if (d8.operation == INSERT && d8.text.empty()) { - return listRet; - } - listRet.push_back(d8); - - if (d9.operation == INSERT && d9.text.empty()) { - return listRet; - } - listRet.push_back(d9); - - if (d10.operation == INSERT && d10.text.empty()) { - return listRet; - } - listRet.push_back(d10); - - return listRet; - } - - Strings diff_halfMatch(const string_t &text1, const string_t &text2) { - Strings list; - HalfMatchResult hm; - if (diff_match_patch::diff_halfMatch(text1, text2, hm)) { - list.push_back(hm.text1_a); - list.push_back(hm.text1_b); - list.push_back(hm.text2_a); - list.push_back(hm.text2_b); - list.push_back(hm.mid_common); - } - return list; - } - - static string_t join(const Strings &list, char delim) { - string_t s; - for (Strings::const_iterator i = list.begin(); i != list.end(); ++i) { - if (i != list.begin()) s += delim; - s += *i; - } - return s; - } - - static Strings split(const string_t& str, char delim) { - Strings list; - string_t::size_type token_len; - for (string_t::const_pointer token = str.c_str(), end = token + str.length(), p; token < end; token += token_len + 1) { - for (p = token; p != end; ++p) if (*p == delim) break; - list.push_back(string_t(token, token_len = p - token)); - } - return list; - } -}; - - -int main() { - diff_match_patch_test dmp_test; - cout << "Starting diff_match_patch unit tests." << endl; - dmp_test.run_all_tests(); - cout << "Done." << endl; - return 0; -} - - -/* -Compile instructions for MinGW on Windows: -g++ -O2 -o diff_match_patch_test diff_match_patch_test.cpp -diff_match_patch_test.exe -*/ diff --git a/libraries/fc/vendor/diff-match-patch-cpp-stl/speedtest.cpp b/libraries/fc/vendor/diff-match-patch-cpp-stl/speedtest.cpp deleted file mode 100644 index b4af090f0e..0000000000 --- a/libraries/fc/vendor/diff-match-patch-cpp-stl/speedtest.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2011 Google Inc. -// All Rights Reserved. -// Author: fraser@google.com - -#include -#include -#include -#include "diff_match_patch.h" - -using namespace std; - -wstring readFile(const char *filename) { - wifstream file(filename); - wstring text; - getline(file, text, wstring::traits_type::to_char_type(wstring::traits_type::eof())); - return text; -} - -int main(int /* argc */, char ** /* argv */) { - wstring text1 = readFile("speedtest1.txt"); - wstring text2 = readFile("speedtest2.txt"); - - diff_match_patch dmp; - dmp.Diff_Timeout = 0.0; - - // Execute one reverse diff as a warmup. - dmp.diff_main(text2, text1, false); - - clock_t t = clock(); - dmp.diff_main(text1, text2, false); - cout << "Elapsed time: " << int((clock() - t) * 1000 / CLOCKS_PER_SEC) << " ms" << endl; - return 0; -} diff --git a/libraries/fc/vendor/equihash/CMakeLists.txt b/libraries/fc/vendor/equihash/CMakeLists.txt index e0606b68ff..6ab89c44c8 100644 --- a/libraries/fc/vendor/equihash/CMakeLists.txt +++ b/libraries/fc/vendor/equihash/CMakeLists.txt @@ -1,7 +1,5 @@ file(GLOB HEADERS "include/equihash/*.hpp" ) -set( CMAKE_C_FLAGS "-std=c99" ) - add_library( equihash src/pow.cpp src/blake2b.c diff --git a/libraries/fc/vendor/websocketpp/CMakeLists.txt b/libraries/fc/vendor/websocketpp/CMakeLists.txt index 2786aba9ee..4f93e243a1 100644 --- a/libraries/fc/vendor/websocketpp/CMakeLists.txt +++ b/libraries/fc/vendor/websocketpp/CMakeLists.txt @@ -24,7 +24,7 @@ endif () ############ Project name and version set (WEBSOCKETPP_MAJOR_VERSION 0) set (WEBSOCKETPP_MINOR_VERSION 8) -set (WEBSOCKETPP_PATCH_VERSION 0) +set (WEBSOCKETPP_PATCH_VERSION 2) set (WEBSOCKETPP_VERSION ${WEBSOCKETPP_MAJOR_VERSION}.${WEBSOCKETPP_MINOR_VERSION}.${WEBSOCKETPP_PATCH_VERSION}) if(POLICY CMP0048) @@ -123,7 +123,11 @@ if (BUILD_TESTS OR BUILD_EXAMPLES) # g++ if (CMAKE_COMPILER_IS_GNUCXX) - set (WEBSOCKETPP_PLATFORM_LIBS pthread rt) + if (NOT APPLE) + set (WEBSOCKETPP_PLATFORM_LIBS pthread rt) + else() + set (WEBSOCKETPP_PLATFORM_LIBS pthread) + endif() set (WEBSOCKETPP_PLATFORM_TLS_LIBS ssl crypto) set (WEBSOCKETPP_BOOST_LIBS system thread) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") @@ -202,7 +206,7 @@ if (BUILD_TESTS OR BUILD_EXAMPLES) endif () if (NOT Boost_USE_STATIC_LIBS) - add_definitions (/DBOOST_TEST_DYN_LINK) + add_definitions (-DBOOST_TEST_DYN_LINK) endif () set (Boost_FIND_REQUIRED TRUE) diff --git a/libraries/fc/vendor/websocketpp/Doxyfile b/libraries/fc/vendor/websocketpp/Doxyfile index e2061659b9..20b695cc9d 100644 --- a/libraries/fc/vendor/websocketpp/Doxyfile +++ b/libraries/fc/vendor/websocketpp/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = WebSocket++ # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.8.1 +PROJECT_NUMBER = 0.8.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/libraries/fc/vendor/websocketpp/changelog.md b/libraries/fc/vendor/websocketpp/changelog.md index 6771d6e9ef..3488a1981e 100644 --- a/libraries/fc/vendor/websocketpp/changelog.md +++ b/libraries/fc/vendor/websocketpp/changelog.md @@ -1,5 +1,17 @@ HEAD +0.8.2 - 2020-04-19 +- Examples: Update print_client_tls example to remove use of deprecated + OpenSSL functions. +- Compatibility: Removes the use of make_shared in a number of cases where + it would be incompatible with newer versions of ASIO. Thank you Stefan + Floeren for the patch. #810 #814 #862 #843 #794 #808 +- CMake: Update cmake installer to better handle dependencies when using + g++ on MacOS. Thank you Luca Palano for reporting and a patch. #831 +- CMake: Update cmake installer to use a variable for the include directory + improving the ability of the install to be customized. THank you Schrijvers + Luc and Gianfranco Costamanga for reporting and a patch. #842 + 0.8.1 - 2018-07-16 Note: This release does not change library behavior. It only corrects issues in the installer and test system. diff --git a/libraries/fc/vendor/websocketpp/cmake/CMakeHelpers.cmake b/libraries/fc/vendor/websocketpp/cmake/CMakeHelpers.cmake index 1478f4b525..f6036325b2 100644 --- a/libraries/fc/vendor/websocketpp/cmake/CMakeHelpers.cmake +++ b/libraries/fc/vendor/websocketpp/cmake/CMakeHelpers.cmake @@ -80,7 +80,7 @@ macro (final_target) endif () install (DIRECTORY ${CMAKE_SOURCE_DIR}/${TARGET_NAME} - DESTINATION include/ + DESTINATION ${INSTALL_INCLUDE_DIR}/ FILES_MATCHING PATTERN "*.hpp*") endmacro () diff --git a/libraries/fc/vendor/websocketpp/docs/faq.dox b/libraries/fc/vendor/websocketpp/docs/faq.dox index 24059f755e..9f417ec410 100644 --- a/libraries/fc/vendor/websocketpp/docs/faq.dox +++ b/libraries/fc/vendor/websocketpp/docs/faq.dox @@ -55,7 +55,7 @@ If you handle errors from methods like send, ping, close, etc correctly then you Normally, for security purposes, operating systems prevent programs from listening on sockets created by other programs. When your program crashes and restarts, the new instance is a different program from the perspective of the operating system. As such it can’t listen on the socket address/port that the previous program was using until after a timeout occurs to make sure the old program was done with it. -The the first step for handling this is to make sure that you provide a method (signal handler, admin websocket message, etc) to perform a clean server shutdown. There is a question elsewhere in this FAQ that describes the steps necessary for this. +The first step for handling this is to make sure that you provide a method (signal handler, admin websocket message, etc) to perform a clean server shutdown. There is a question elsewhere in this FAQ that describes the steps necessary for this. The clean close strategy won't help in the case of crashes or other abnormal closures. An option to consider for these cases is the use of the SO_REUSEADDR socket option. This instructs the OS to not request an exclusive lock on the socket. This means that after your program crashes the replacement you start can immediately listen on that address/port combo again. diff --git a/libraries/fc/vendor/websocketpp/examples/print_client_tls/print_client_tls.cpp b/libraries/fc/vendor/websocketpp/examples/print_client_tls/print_client_tls.cpp index 0a1c72c06d..469d9c61e3 100644 --- a/libraries/fc/vendor/websocketpp/examples/print_client_tls/print_client_tls.cpp +++ b/libraries/fc/vendor/websocketpp/examples/print_client_tls/print_client_tls.cpp @@ -61,7 +61,7 @@ bool verify_subject_alternative_name(const char * hostname, X509 * cert) { continue; } - char * dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName); + char const * dns_name = (char const *) ASN1_STRING_get0_data(current_name->d.dNSName); // Make sure there isn't an embedded NUL character in the DNS name if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) { @@ -76,7 +76,7 @@ bool verify_subject_alternative_name(const char * hostname, X509 * cert) { } /// Verify that the certificate common name matches the given hostname -bool verify_common_name(const char * hostname, X509 * cert) { +bool verify_common_name(char const * hostname, X509 * cert) { // Find the position of the CN field in the Subject field of the certificate int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1); if (common_name_loc < 0) { @@ -95,7 +95,7 @@ bool verify_common_name(const char * hostname, X509 * cert) { return false; } - char * common_name_str = (char *) ASN1_STRING_data(common_name_asn1); + char const * common_name_str = (char const *) ASN1_STRING_get0_data(common_name_asn1); // Make sure there isn't an embedded NUL character in the CN if (ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) { diff --git a/libraries/fc/vendor/websocketpp/readme.md b/libraries/fc/vendor/websocketpp/readme.md index aa99f668e2..6cc608516f 100644 --- a/libraries/fc/vendor/websocketpp/readme.md +++ b/libraries/fc/vendor/websocketpp/readme.md @@ -1,4 +1,4 @@ -WebSocket++ (0.8.1) +WebSocket++ (0.8.2) ========================== WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket diff --git a/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/connection.hpp b/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/connection.hpp index c431230010..ae39d509aa 100644 --- a/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/connection.hpp +++ b/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/connection.hpp @@ -312,9 +312,10 @@ class connection : public config::socket_type::socket_con_type { * needed. */ timer_ptr set_timer(long duration, timer_handler callback) { - timer_ptr new_timer = lib::make_shared( - lib::ref(*m_io_service), - lib::asio::milliseconds(duration) + timer_ptr new_timer( + new lib::asio::steady_timer( + *m_io_service, + lib::asio::milliseconds(duration)) ); if (config::enable_multithreading) { @@ -462,8 +463,7 @@ class connection : public config::socket_type::socket_con_type { m_io_service = io_service; if (config::enable_multithreading) { - m_strand = lib::make_shared( - lib::ref(*io_service)); + m_strand.reset(new lib::asio::io_service::strand(*io_service)); } lib::error_code ec = socket_con_type::init_asio(io_service, m_strand, diff --git a/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/endpoint.hpp b/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/endpoint.hpp index ddab2c7421..94509adb3b 100644 --- a/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/endpoint.hpp +++ b/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/endpoint.hpp @@ -195,8 +195,7 @@ class endpoint : public config::socket_type { m_io_service = ptr; m_external_io_service = true; - m_acceptor = lib::make_shared( - lib::ref(*m_io_service)); + m_acceptor.reset(new lib::asio::ip::tcp::acceptor(*m_io_service)); m_state = READY; ec = lib::error_code(); @@ -688,9 +687,7 @@ class endpoint : public config::socket_type { * @since 0.3.0 */ void start_perpetual() { - m_work = lib::make_shared( - lib::ref(*m_io_service) - ); + m_work.reset(new lib::asio::io_service::work(*m_io_service)); } /// Clears the endpoint's perpetual flag, allowing it to exit when empty @@ -854,8 +851,7 @@ class endpoint : public config::socket_type { // Create a resolver if (!m_resolver) { - m_resolver = lib::make_shared( - lib::ref(*m_io_service)); + m_resolver.reset(new lib::asio::ip::tcp::resolver(*m_io_service)); } tcon->set_uri(u); diff --git a/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/security/none.hpp b/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/security/none.hpp index 0d8c5b1590..6c7d352410 100644 --- a/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/security/none.hpp +++ b/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/security/none.hpp @@ -156,8 +156,7 @@ class connection : public lib::enable_shared_from_this { /// Perform one time initializations /** * init_asio is called once immediately after construction to initialize - * Asio components to the io_service. At this stage the connection is - * speculative, the server may not have actually received a new connection. + * Asio components to the io_service * * @param service A pointer to the endpoint's io_service * @param strand A shared pointer to the connection's asio strand @@ -169,8 +168,11 @@ class connection : public lib::enable_shared_from_this { return socket::make_error_code(socket::error::invalid_state); } - m_socket = lib::make_shared( - lib::ref(*service)); + m_socket.reset(new lib::asio::ip::tcp::socket(*service)); + + if (m_socket_init_handler) { + m_socket_init_handler(m_hdl, *m_socket); + } m_state = READY; @@ -192,7 +194,7 @@ class connection : public lib::enable_shared_from_this { /// Pre-initialize security policy /** - * Called by the transport after a new connection is accepted to initialize + * Called by the transport after a new connection is created to initialize * the socket component of the connection. This method is not allowed to * write any bytes to the wire. This initialization happens before any * proxies or other intermediate wrappers are negotiated. @@ -205,10 +207,6 @@ class connection : public lib::enable_shared_from_this { return; } - if (m_socket_init_handler) { - m_socket_init_handler(m_hdl, *m_socket); - } - m_state = READING; callback(lib::error_code()); diff --git a/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/security/tls.hpp b/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/security/tls.hpp index 98231d4c21..04ac379036 100644 --- a/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/security/tls.hpp +++ b/libraries/fc/vendor/websocketpp/websocketpp/transport/asio/security/tls.hpp @@ -193,8 +193,11 @@ class connection : public lib::enable_shared_from_this { if (!m_context) { return socket::make_error_code(socket::error::invalid_tls_context); } - m_socket = lib::make_shared( - _WEBSOCKETPP_REF(*service),lib::ref(*m_context)); + m_socket.reset(new socket_type(*service, *m_context)); + + if (m_socket_init_handler) { + m_socket_init_handler(m_hdl, get_socket()); + } m_io_service = service; m_strand = strand; @@ -244,9 +247,6 @@ class connection : public lib::enable_shared_from_this { } } #endif - if (m_socket_init_handler) { - m_socket_init_handler(m_hdl, get_socket()); - } callback(lib::error_code()); } diff --git a/libraries/fc/vendor/websocketpp/websocketpp/version.hpp b/libraries/fc/vendor/websocketpp/websocketpp/version.hpp index f701ab103b..5766546f76 100644 --- a/libraries/fc/vendor/websocketpp/websocketpp/version.hpp +++ b/libraries/fc/vendor/websocketpp/websocketpp/version.hpp @@ -44,7 +44,7 @@ static int const major_version = 0; /// Library minor version number static int const minor_version = 8; /// Library patch version number -static int const patch_version = 1; +static int const patch_version = 2; /// Library pre-release flag /** * This is a textual flag indicating the type and number for pre-release @@ -54,7 +54,7 @@ static int const patch_version = 1; static char const prerelease_flag[] = ""; /// Default user agent string -static char const user_agent[] = "WebSocket++/0.8.1"; +static char const user_agent[] = "WebSocket++/0.8.2"; } // namespace websocketpp diff --git a/libraries/legacy_plugins/account_statistics/include/hive/account_statistics/account_statistics_plugin.hpp b/libraries/legacy_plugins/account_statistics/include/hive/account_statistics/account_statistics_plugin.hpp index 0e3d97dc70..ee153dd5c3 100644 --- a/libraries/legacy_plugins/account_statistics/include/hive/account_statistics/account_statistics_plugin.hpp +++ b/libraries/legacy_plugins/account_statistics/include/hive/account_statistics/account_statistics_plugin.hpp @@ -91,6 +91,7 @@ struct account_stats_bucket_object : public object< account_stats_bucket_object_ share_type hbd_to_be_converted = 0; ///< Amount of HBD to be converted uint32_t hbd_conversion_requests_filled = 0; ///< HBD conversion requests filled share_type hive_converted = 0; ///< Amount of HIVE that was converted + //note: collateralized convert requests not handled here - plugin is assumed obsolete uint32_t limit_orders_created = 0; ///< Limit orders created by this account uint32_t limit_orders_filled = 0; ///< Limit orders filled by this account uint32_t limit_orders_cancelled = 0; ///< Limit orders cancelled by this account diff --git a/libraries/legacy_plugins/block_info/block_info_plugin.cpp b/libraries/legacy_plugins/block_info/block_info_plugin.cpp index 0d893ab1e2..dedc486227 100644 --- a/libraries/legacy_plugins/block_info/block_info_plugin.cpp +++ b/libraries/legacy_plugins/block_info/block_info_plugin.cpp @@ -48,7 +48,7 @@ void block_info_plugin::on_applied_block( const chain::signed_block& b ) info.block_id = b.id(); info.block_size = fc::raw::pack_size( b ); info.aslot = dgpo.current_aslot; - info.last_irreversible_block_num = dgpo.last_irreversible_block_num; + info.last_irreversible_block_num = db.get_last_irreversible_block_num(); info.num_pow_witnesses = dgpo.num_pow_witnesses; return; } diff --git a/libraries/legacy_plugins/blockchain_statistics/blockchain_statistics_plugin.cpp b/libraries/legacy_plugins/blockchain_statistics/blockchain_statistics_plugin.cpp index fe7976d4e6..7b7398bb1d 100644 --- a/libraries/legacy_plugins/blockchain_statistics/blockchain_statistics_plugin.cpp +++ b/libraries/legacy_plugins/blockchain_statistics/blockchain_statistics_plugin.cpp @@ -234,6 +234,11 @@ struct operation_process }); } + void operator()( const collateralized_convert_operation& op )const + { + FC_ASSERT( false && "Unhandled, plugin assumed to be obsolete" ); + } + void operator()( const fill_convert_request_operation& op )const { _db.modify( _bucket, [&]( bucket_object& b ) @@ -242,6 +247,11 @@ struct operation_process b.hive_converted += op.amount_out.amount; }); } + + void operator()( const fill_collateralized_convert_request_operation& op )const + { + FC_ASSERT( false && "Unhandled, plugin assumed to be obsolete" ); + } }; void blockchain_statistics_plugin_impl::on_block( const signed_block& b ) diff --git a/libraries/legacy_plugins/blockchain_statistics/include/hive/blockchain_statistics/blockchain_statistics_api.hpp b/libraries/legacy_plugins/blockchain_statistics/include/hive/blockchain_statistics/blockchain_statistics_api.hpp index ad0e4c0679..50c756caaf 100644 --- a/libraries/legacy_plugins/blockchain_statistics/include/hive/blockchain_statistics/blockchain_statistics_api.hpp +++ b/libraries/legacy_plugins/blockchain_statistics/include/hive/blockchain_statistics/blockchain_statistics_api.hpp @@ -64,6 +64,7 @@ struct statistics share_type hbd_to_be_converted = 0; ///< Amount of HBD to be converted uint32_t hbd_conversion_requests_filled = 0; ///< HBD conversion requests filled share_type hive_converted = 0; ///< Amount of HIVE that was converted + //note: collateralized convert requests not handled here - plugin is assumed obsolete uint32_t limit_orders_created = 0; ///< Limit orders created uint32_t limit_orders_filled = 0; ///< Limit orders filled uint32_t limit_orders_cancelled = 0; ///< Limit orders cancelled diff --git a/libraries/legacy_plugins/blockchain_statistics/include/hive/blockchain_statistics/blockchain_statistics_plugin.hpp b/libraries/legacy_plugins/blockchain_statistics/include/hive/blockchain_statistics/blockchain_statistics_plugin.hpp index b2525b7090..7ef41deabb 100644 --- a/libraries/legacy_plugins/blockchain_statistics/include/hive/blockchain_statistics/blockchain_statistics_plugin.hpp +++ b/libraries/legacy_plugins/blockchain_statistics/include/hive/blockchain_statistics/blockchain_statistics_plugin.hpp @@ -89,23 +89,24 @@ struct bucket_object : public object< bucket_object_type, bucket_object > uint32_t new_reply_votes = 0; ///< New votes on replies uint32_t changed_reply_votes = 0; ///< Changed votes on replies uint32_t payouts = 0; ///< Number of comment payouts - share_type hbd_paid_to_authors = 0; ///< Ammount of HBD paid to authors - share_type vests_paid_to_authors = 0; ///< Ammount of VESS paid to authors - share_type vests_paid_to_curators = 0; ///< Ammount of VESTS paid to curators - share_type liquidity_rewards_paid = 0; ///< Ammount of HIVE paid to market makers + share_type hbd_paid_to_authors = 0; ///< Amount of HBD paid to authors + share_type vests_paid_to_authors = 0; ///< Amount of VESS paid to authors + share_type vests_paid_to_curators = 0; ///< Amount of VESTS paid to curators + share_type liquidity_rewards_paid = 0; ///< Amount of HIVE paid to market makers uint32_t transfers_to_vesting = 0; ///< Transfers of HIVE into VESTS - share_type hive_vested = 0; ///< Ammount of HIVE vested + share_type hive_vested = 0; ///< Amount of HIVE vested uint32_t new_vesting_withdrawal_requests = 0; ///< New vesting withdrawal requests uint32_t modified_vesting_withdrawal_requests = 0; ///< Changes to vesting withdrawal requests share_type vesting_withdraw_rate_delta = 0; uint32_t vesting_withdrawals_processed = 0; ///< Number of vesting withdrawals uint32_t finished_vesting_withdrawals = 0; ///< Processed vesting withdrawals that are now finished - share_type vests_withdrawn = 0; ///< Ammount of VESTS withdrawn to HIVE - share_type vests_transferred = 0; ///< Ammount of VESTS transferred to another account + share_type vests_withdrawn = 0; ///< Amount of VESTS withdrawn to HIVE + share_type vests_transferred = 0; ///< Amount of VESTS transferred to another account uint32_t hbd_conversion_requests_created = 0; ///< HBD conversion requests created share_type hbd_to_be_converted = 0; ///< Amount of HBD to be converted uint32_t hbd_conversion_requests_filled = 0; ///< HBD conversion requests filled share_type hive_converted = 0; ///< Amount of HIVE that was converted + //note: collateralized convert requests not handled here - plugin is assumed obsolete uint32_t limit_orders_created = 0; ///< Limit orders created uint32_t limit_orders_filled = 0; ///< Limit orders filled uint32_t limit_orders_cancelled = 0; ///< Limit orders cancelled diff --git a/libraries/legacy_plugins/delayed_node/delayed_node_plugin.cpp b/libraries/legacy_plugins/delayed_node/delayed_node_plugin.cpp index 33d4c7d595..8088a8d67d 100644 --- a/libraries/legacy_plugins/delayed_node/delayed_node_plugin.cpp +++ b/libraries/legacy_plugins/delayed_node/delayed_node_plugin.cpp @@ -88,7 +88,8 @@ void delayed_node_plugin::sync_with_trusted_node() uint32_t pass_count = 0; while( true ) { - hive::chain::dynamic_global_property_object remote_dpo = my->database_api->get_dynamic_global_properties(); + api_dynamic_global_property_object remote_dpo = my->database_api->get_dynamic_global_properties(); + //hive::chain::dynamic_global_property_object remote_dpo = my->database_api->get_dynamic_global_properties(); if( remote_dpo.last_irreversible_block_num <= db.head_block_num() ) { if( remote_dpo.last_irreversible_block_num < db.head_block_num() ) diff --git a/libraries/mira/CMakeLists.txt b/libraries/mira/CMakeLists.txt deleted file mode 100644 index e2a024c4e4..0000000000 --- a/libraries/mira/CMakeLists.txt +++ /dev/null @@ -1,74 +0,0 @@ -# Defines MIRA library target. -project( MIRA ) -cmake_minimum_required( VERSION 2.8.12 ) - -file(GLOB HEADERS "include/mira/*.hpp" "include/mira/detail/*.hpp") - -set(CMAKE_EXPORT_COMPILE_COMMANDS "ON") -SET(BOOST_COMPONENTS) -LIST(APPEND BOOST_COMPONENTS thread - date_time - system - filesystem - chrono - unit_test_framework - locale) - -FIND_PACKAGE(Boost 1.58 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) -set( Boost_USE_STATIC_LIBS ON CACHE STRING "ON or OFF" ) - -if( APPLE ) - # Apple Specific Options Here - message( STATUS "Configuring MIRA on OS X" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++14 -stdlib=libc++ -Wall" ) -else( APPLE ) - # Linux Specific Options Here - message( STATUS "Configuring MIRA on Linux" ) - set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++14 -Wall" ) - set( rt_library rt ) - set( pthread_library pthread) - if ( FULL_STATIC_BUILD ) - set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") - endif ( FULL_STATIC_BUILD ) -endif( APPLE ) - -if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp" ) -endif() - -if(ENABLE_COVERAGE_TESTING) - SET(CMAKE_CXX_FLAGS "--coverage ${CMAKE_CXX_FLAGS}") -endif() - -add_library( mira - src/mira.cpp - src/configuration.cpp - ${HEADERS} - ) - -if( CLANG_TIDY_EXE ) - set_target_properties( - mira PROPERTIES - CXX_CLANG_TIDY "${DO_CLANG_TIDY}" - ) -endif( CLANG_TIDY_EXE ) - -target_link_libraries( mira ${Boost_LIBRARIES} rocksdb fc ) - -target_include_directories( mira - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" - "${CMAKE_CURRENT_SOURCE_DIR}/../vendor/rocksdb/include" - ${Boost_INCLUDE_DIR} - ) - -add_subdirectory( test ) - -INSTALL( TARGETS - mira - - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib -) -INSTALL( FILES ${HEADERS} DESTINATION "include/mira" ) - diff --git a/libraries/mira/README.md b/libraries/mira/README.md deleted file mode 100644 index 9a8f521932..0000000000 --- a/libraries/mira/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# MIRA - Multi-Index RocksDB Adapter - -MIRA is a Boost Multi-Index Container wrapper around RocksDB. MIRA Multi-Index Container can be written -and expose equivallent Boost and RocksDB implementations. diff --git a/libraries/mira/include/mira/boost_adapter.hpp b/libraries/mira/include/mira/boost_adapter.hpp deleted file mode 100644 index 43cea9f08e..0000000000 --- a/libraries/mira/include/mira/boost_adapter.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once -#include - -namespace mira { - -template< typename Value, typename IndexSpecifierList, typename Allocator > -class boost_multi_index_adapter : public boost::multi_index_container< Value, IndexSpecifierList, Allocator > -{ - private: - typedef typename Value::id_type id_type; - int64_t _revision = -1; - id_type _next_id = 0; - - public: - using boost::multi_index_container< Value, IndexSpecifierList, Allocator >::multi_index_container; - static const size_t node_size = sizeof( Value ); - - int64_t revision() - { - return _revision; - } - - int64_t set_revision( uint64_t revision ) - { - _revision = revision; - return _revision; - } - - id_type next_id() - { - return _next_id; - } - - void set_next_id( id_type id ) - { - _next_id = id; - } - - void close() {} - void wipe( const boost::filesystem::path& p ) {} - void flush() {} - bool open( const boost::filesystem::path& p, const boost::any& opts ) { return true; } - void trim_cache() {} - - void print_stats() const {} - - size_t get_cache_usage() const { return 0; } - size_t get_cache_size() const { return 0; } - void dump_lb_call_counts() {} - - template< typename MetaKey, typename MetaValue > - bool get_metadata( const MetaKey& k, MetaValue& v ) { return true; } - - template< typename MetaKey, typename MetaValue > - bool put_metadata( const MetaKey& k, const MetaValue& v ) { return true; } -}; - -} // mira diff --git a/libraries/mira/include/mira/composite_key.hpp b/libraries/mira/include/mira/composite_key.hpp deleted file mode 100644 index 6023512eb7..0000000000 --- a/libraries/mira/include/mira/composite_key.hpp +++ /dev/null @@ -1,1653 +0,0 @@ -/* Copyright 2003-2015 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include -#include - -#include /* keep it first to prevent nasty warns in MSVC */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING) -#include -#endif - -#if !defined(BOOST_NO_SFINAE) -#include -#endif - -#if !defined(BOOST_NO_CXX11_HDR_TUPLE)&&\ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -#include -#endif - -/* A composite key stores n key extractors and "computes" the - * result on a given value as a packed reference to the value and - * the composite key itself. Actual invocations to the component - * key extractors are lazily performed when executing an operation - * on composite_key results (equality, comparison, hashing.) - * As the other key extractors in Boost.MultiIndex, composite_key - * is overloaded to work on chained pointers to T and reference_wrappers - * of T. - */ - -/* This user_definable macro limits the number of elements of a composite - * key; useful for shortening resulting symbol names (MSVC++ 6.0, for - * instance has problems coping with very long symbol names.) - * NB: This cannot exceed the maximum number of arguments of - * boost::tuple. In Boost 1.32, the limit is 10. - */ - -#if !defined(BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE) -#define BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE 10 -#endif - -/* maximum number of key extractors in a composite key */ - -#if BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE<10 /* max length of a tuple */ -#define BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE \ - BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE -#else -#define BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE 10 -#endif - -/* BOOST_PP_ENUM of BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE elements */ - -#define BOOST_MULTI_INDEX_CK_ENUM(macro,data) \ - BOOST_PP_ENUM(BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE,macro,data) - -/* BOOST_PP_ENUM_PARAMS of BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE elements */ - -#define BOOST_MULTI_INDEX_CK_ENUM_PARAMS(param) \ - BOOST_PP_ENUM_PARAMS(BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE,param) - -/* if n==0 -> text0 - * otherwise -> textn=tuples::null_type - */ - -#define BOOST_MULTI_INDEX_CK_TEMPLATE_PARM(z,n,text) \ - typename BOOST_PP_CAT(text,n) BOOST_PP_EXPR_IF(n,=tuples::null_type) - -/* const textn& kn=textn() */ - -#define BOOST_MULTI_INDEX_CK_CTOR_ARG(z,n,text) \ - const BOOST_PP_CAT(text,n)& BOOST_PP_CAT(k,n) = BOOST_PP_CAT(text,n)() - -/* typename list(0)::type */ - -#define BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N(z,n,list) \ - BOOST_DEDUCED_TYPENAME BOOST_PP_LIST_AT(list,0)< \ - BOOST_PP_LIST_AT(list,1),n \ - >::type - -namespace mira{ - -template class reference_wrapper; /* fwd decl. */ - -namespace multi_index{ - -namespace detail{ - -/* n-th key extractor of a composite key */ - -template -struct nth_key_from_value -{ - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef typename boost::mpl::eval_if_c< - N::value, - boost::tuples::element, - boost::mpl::identity - >::type type; -}; - -/* nth_composite_key_##name::type yields - * functor >, or tuples::null_type - * if N exceeds the length of the composite key. - */ - -#define BOOST_MULTI_INDEX_CK_NTH_COMPOSITE_KEY_FUNCTOR(name,functor) \ -template \ -struct BOOST_PP_CAT(key_,name) \ -{ \ - typedef functor type; \ -}; \ - \ -template<> \ -struct BOOST_PP_CAT(key_,name) \ -{ \ - typedef boost::tuples::null_type type; \ -}; \ - \ -template \ -struct BOOST_PP_CAT(nth_composite_key_,name) \ -{ \ - typedef typename nth_key_from_value::type key_from_value; \ - typedef typename BOOST_PP_CAT(key_,name)::type type; \ -}; - -/* nth_composite_key_equal_to - * nth_composite_key_less - * nth_composite_key_greater - * nth_composite_key_hash - */ - -BOOST_MULTI_INDEX_CK_NTH_COMPOSITE_KEY_FUNCTOR(equal_to,std::equal_to) -BOOST_MULTI_INDEX_CK_NTH_COMPOSITE_KEY_FUNCTOR(less,std::less) -BOOST_MULTI_INDEX_CK_NTH_COMPOSITE_KEY_FUNCTOR(greater,std::greater) -BOOST_MULTI_INDEX_CK_NTH_COMPOSITE_KEY_FUNCTOR(hash,boost::hash) - -/* used for defining equality and comparison ops of composite_key_result */ - -#define BOOST_MULTI_INDEX_CK_IDENTITY_ENUM_MACRO(z,n,text) text - -struct generic_operator_equal -{ - template - bool operator()(const T& x,const Q& y)const{return x==y;} -}; - -typedef boost::tuple< - BOOST_MULTI_INDEX_CK_ENUM( - BOOST_MULTI_INDEX_CK_IDENTITY_ENUM_MACRO, - detail::generic_operator_equal)> generic_operator_equal_tuple; - -struct generic_operator_less -{ - template - bool operator()(const T& x,const Q& y)const{return x generic_operator_less_tuple; - -/* Metaprogramming machinery for implementing equality, comparison and - * hashing operations of composite_key_result. - * - * equal_* checks for equality between composite_key_results and - * between those and tuples, accepting a tuple of basic equality functors. - * compare_* does lexicographical comparison. - * hash_* computes a combination of elementwise hash values. - */ - -template< typename KeyCons1, typename KeyCons2, typename EqualCons > -struct equal_key_key; /* fwd decl. */ - -template< typename KeyCons1, typename KeyCons2, typename EqualCons > -struct equal_key_key_terminal -{ - static bool compare( - const KeyCons1&, - const KeyCons2&, - const EqualCons& ) - { - return true; - } -}; - -template< typename KeyCons1, typename KeyCons2, typename EqualCons > -struct equal_key_key_normal -{ - static bool compare( - const KeyCons1& k1, - const KeyCons2& k2, - const EqualCons& eq ) - { - if( !eq.get_head()( k1.get_head(), k2.get_head() ) ) return false; - return equal_key_key< - typename KeyCons1::tail_type, - typename KeyCons2::tail_type, - typename EqualCons::tail_type - >::compare( k1.get_tail(), k2.get_tail(), eq.get_tail() ); - } -}; - -template< typename KeyCons1, typename KeyCons2, typename EqualCons > -struct equal_key_key: - boost::mpl::if_< - boost::mpl::or_< - boost::is_same< KeyCons1, boost::tuples::null_type >, - boost::is_same< KeyCons2, boost::tuples::null_type > - >, - equal_key_key_terminal< KeyCons1, KeyCons2, EqualCons >, - equal_key_key_normal< KeyCons1, KeyCons2 ,EqualCons > - >::type -{}; - -template< typename KeyCons1, typename KeyCons2, typename CompareCons > -struct compare_key_key; /* fwd decl. */ - -template< typename KeyCons1, typename KeyCons2, typename CompareCons > -struct compare_key_key_terminal -{ - static bool compare( const KeyCons1&, const KeyCons2&, const CompareCons& ) - { - return false; - } -}; - -template< typename KeyCons1, typename KeyCons2, typename CompareCons > -struct compare_key_key_normal -{ - static bool compare( - const KeyCons1& k1, - const KeyCons2& k2, - const CompareCons& comp) - { - if(comp.get_head()(k1.get_head(),k2.get_head()))return true; - if(comp.get_head()(k2.get_head(),k1.get_head()))return false; - return compare_key_key< - typename KeyCons1::tail_type, - typename KeyCons2::tail_type, - typename CompareCons::tail_type - >::compare(k1.get_tail(),k2.get_tail(),comp.get_tail()); - } -}; - -template< typename KeyCons1, typename KeyCons2, typename CompareCons > -struct compare_key_key: - boost::mpl::if_< - boost::mpl::or_< - boost::is_same, - boost::is_same - >, - compare_key_key_terminal< KeyCons1, KeyCons2, CompareCons >, - compare_key_key_normal< KeyCons1, KeyCons2, CompareCons > - >::type -{}; - -template -struct hash_ckey; /* fwd decl. */ - -template -struct hash_ckey_terminal -{ - static std::size_t hash( - const KeyCons&,const Value&,const HashCons&,std::size_t carry) - { - return carry; - } -}; - -template -struct hash_ckey_normal -{ - static std::size_t hash( - const KeyCons& c,const Value& v,const HashCons& h,std::size_t carry=0) - { - /* same hashing formula as boost::hash_combine */ - - carry^=h.get_head()(c.get_head()(v))+0x9e3779b9+(carry<<6)+(carry>>2); - return hash_ckey< - BOOST_DEDUCED_TYPENAME KeyCons::tail_type,Value, - BOOST_DEDUCED_TYPENAME HashCons::tail_type - >::hash(c.get_tail(),v,h.get_tail(),carry); - } -}; - -template -struct hash_ckey: - boost::mpl::if_< - boost::is_same, - hash_ckey_terminal, - hash_ckey_normal - >::type -{ -}; - -template -struct hash_cval; /* fwd decl. */ - -template -struct hash_cval_terminal -{ - static std::size_t hash(const ValCons&,const HashCons&,std::size_t carry) - { - return carry; - } -}; - -template -struct hash_cval_normal -{ - static std::size_t hash( - const ValCons& vc,const HashCons& h,std::size_t carry=0) - { - carry^=h.get_head()(vc.get_head())+0x9e3779b9+(carry<<6)+(carry>>2); - return hash_cval< - BOOST_DEDUCED_TYPENAME ValCons::tail_type, - BOOST_DEDUCED_TYPENAME HashCons::tail_type - >::hash(vc.get_tail(),h.get_tail(),carry); - } -}; - -template -struct hash_cval: - boost::mpl::if_< - boost::is_same, - hash_cval_terminal, - hash_cval_normal - >::type -{ -}; - -} /* namespace multi_index::detail */ - -/* composite_key_result */ - -#if defined(BOOST_MSVC) -#pragma warning(push) -#pragma warning(disable:4512) -#endif - -template< typename KeyExtractor, typename Key, typename Value > -struct key_from_value; // Forward Declaration - -template< typename KeyExtractor, typename Key, typename Value > -struct key_from_value_terminal -{ - static Key extract( const KeyExtractor& e, const Value& v ) - { - return boost::tuples::null_type(); - } -}; - -template< typename KeyExtractor, typename Key, typename Value > -struct key_from_value_normal -{ - static Key extract( const KeyExtractor& e, const Value& v ) - { - return Key( - e.get_head()( v ), - key_from_value< - typename KeyExtractor::tail_type, - typename Key::tail_type, - Value - >::extract( e.get_tail(), v ) ); - } -}; - -template< typename KeyExtractor, typename Key, typename Value > -struct key_from_value : - boost::mpl::if_< - boost::is_same< KeyExtractor, boost::tuples::null_type >, - key_from_value_terminal< KeyExtractor, Key, Value >, - key_from_value_normal< KeyExtractor, Key, Value > - >::type -{}; - -template< typename Key, typename Tuple > -struct convert_compatible_key; - -template< typename Key, typename Tuple > -struct convert_compatible_key_terminal -{ - static Key convert( const Tuple& t ) - { - return Key( - t.get_head(), - typename Key::tail_type() ); - } -}; - -template< typename Key, typename Tuple > -struct convert_compatible_key_normal -{ - static Key convert( const Tuple& t ) - { - return Key( - t.get_head(), - convert_compatible_key< - typename Key::tail_type, - typename Tuple::tail_type - >::convert( t.get_tail() ) ); - } -}; - -template< typename Key, typename Tuple > -struct convert_compatible_key : - boost::mpl::if_< - boost::mpl::or_< - boost::is_same< typename Key::tail_type, boost::tuples::null_type >, - boost::is_same< typename Tuple::tail_type, boost::tuples::null_type > - >, - convert_compatible_key_terminal< Key, Tuple >, - convert_compatible_key_normal< Key, Tuple > - >::type -{}; - -/* -template< typename Key, typename CompatibleKey > -struct convert_compatible_key; - -template< typename Key, typename CompKeyHT > -struct convert_compatible_key< Key, boost::tuples::cons< CompKeyHT, boost::tuples::null_type > > -{ - static Key convert( const boost::tuples::cons< CompKeyHT, boost::tuples::null_type >& k ) - { - return Key( k.get_head(), typename Key::tail_type() ); - } -}; - -template< typename Key, typename CompKeyHT, typename CompKeyTT > -struct convert_compatible_key< Key, boost::tuples::cons< CompKeyHT, CompKeyTT > > -{ - static Key convert( const boost::tuples::cons< CompKeyHT, CompKeyTT >& k ) - { - return Key( k.get_head(), k.get_tail() ); - } -}; - -template< typename Key, typename... Args > -struct convert_compatible_key< Key, boost::tuples::tuple< Args... > > -{ - static Key convert( const boost::tuples::tuple< Args... >& k ) - { - return convert_to_cons< Key, boost::tuples::tuple< Args ... > >::convert( k ); - } -}; - -template< typename Key, typename CompatibleKey > -struct convert_compatible_key -{ - static Key convert( const CompatibleKey& k ) - { - return Key( k, typename Key::tail_type() ); - } -}; -*/ - -template< typename KeyCons > -struct composite_key_conversion; // Forward Declaration - -template< typename HT, typename TT > -struct result_type_extractor -{ - typedef typename boost::tuples::cons< - typename HT::result_type, - typename composite_key_conversion< TT >::type - > type; -}; - -template< typename HT > -struct result_type_extractor< HT, boost::tuples::null_type > -{ - typedef typename boost::tuples::cons< - typename HT::result_type, - boost::tuples::null_type - > type; -}; - -template< typename KeyCons > -struct composite_key_conversion -{ - typedef typename result_type_extractor< - typename KeyCons::head_type, - typename KeyCons::tail_type - >::type type; -}; - -template -struct composite_key_result -{ - typedef CompositeKey composite_key_type; - typedef typename composite_key_conversion< - typename composite_key_type::key_extractor_tuple - >::type key_type; - typedef typename composite_key_type::value_type value_type; - - explicit composite_key_result( const composite_key_type& composite_key, const value_type& value ) : - key( - key_from_value< - typename composite_key_type::key_extractor_tuple, - key_type, - value_type - >::extract( composite_key.key_extractors(), value ) ) - {} - - template< typename... Args > - explicit composite_key_result( const boost::tuples::tuple< Args... > compat_key ) : - key( convert_compatible_key< - key_type, - boost::tuples::tuple< Args... > - >::convert( compat_key ) ) - {} - - template< typename CompatibleKey > - explicit composite_key_result( const CompatibleKey& k ) : composite_key_result( boost::make_tuple( k ) ) - {} - - composite_key_result() {} - - key_type key; -}; - -#if defined(BOOST_MSVC) -#pragma warning(pop) -#endif - -/* composite_key */ - -using namespace boost; - -template< - typename Value, - BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_TEMPLATE_PARM,KeyFromValue) -> -struct composite_key: - private tuple -{ -private: - typedef boost::tuple super; - -public: - typedef super key_extractor_tuple; - typedef Value value_type; - typedef composite_key_result result_type; - - composite_key( - BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_CTOR_ARG,KeyFromValue)): - super(BOOST_MULTI_INDEX_CK_ENUM_PARAMS(k)) - {} - - composite_key(const key_extractor_tuple& x):super(x){} - - const key_extractor_tuple& key_extractors()const{return *this;} - key_extractor_tuple& key_extractors(){return *this;} - - template - -#if !defined(BOOST_NO_SFINAE) - typename disable_if< - is_convertible,result_type>::type -#else - result_type -#endif - - operator()(const ChainedPtr& x)const - { - return operator()(*x); - } - - result_type operator()(const value_type& x)const - { - return result_type( *this, x ); - } - - result_type operator()(const reference_wrapper& x)const - { - return result_type( *this, x.get() ); - } - - result_type operator()(const reference_wrapper& x)const - { - return result_type( *this, x.get() ); - } -}; - -/* comparison operators */ - -/* == */ - -template< typename CompositeKey1, typename CompositeKey2 > -inline bool operator==( - const composite_key_result< CompositeKey1 >& x, - const composite_key_result< CompositeKey2 >& y) -{ - typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; - typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value== - boost::tuples::length::value); - - return detail::equal_key_key< - typename CompositeKey1::result_type::key_type, - typename CompositeKey2::result_type::key_type, - detail::generic_operator_equal_tuple - >::compare( - x.key, - y.key, - detail::generic_operator_equal_tuple()); -} - -template< - typename CompositeKey, - BOOST_MULTI_INDEX_CK_ENUM_PARAMS( typename Value ) -> -inline bool operator==( - const composite_key_result< CompositeKey >& x, - const tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) >& y) -{ - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef boost::tuple< - BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) > key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value== - boost::tuples::length::value); - - return detail::equal_key_key< - typename CompositeKey::result_type::key_type, - key_tuple, - detail::generic_operator_equal_tuple - >::compare( - x.key, y, detail::generic_operator_equal_tuple() ); -} - -template -< - BOOST_MULTI_INDEX_CK_ENUM_PARAMS( typename Value ), - typename CompositeKey -> -inline bool operator==( - const tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) >& x, - const composite_key_result< CompositeKey >& y) -{ - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef boost::tuple key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value== - boost::tuples::length::value); - - return detail::equal_key_key< - key_tuple, - typename CompositeKey::result_type::key_type, - detail::generic_operator_equal_tuple - >::compare( - x, y.key, detail::generic_operator_equal_tuple() ); -} - -#if !defined(BOOST_NO_CXX11_HDR_TUPLE)&&\ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -template< typename CompositeKey, typename... Values > -inline bool operator==( - const composite_key_result< CompositeKey >& x, - const std::tuple< Values... >& y) -{ - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - BOOST_STATIC_ASSERT( - static_cast(boost::tuples::length::value)== - std::tuple_size::value); - - return detail::equal_key_key< - typename CompositeKey::result_type::key_type, - cons_key_tuple, - detail::generic_operator_equal_tuple - >::compare( - x.key, detail::make_cons_stdtuple(y), detail::generic_operator_equal_tuple() ); -} - -template< typename CompositeKey, typename... Values > -inline bool operator==( - const std::tuple< Values... >& x, - const composite_key_result< CompositeKey >& y) -{ - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - BOOST_STATIC_ASSERT( - static_cast(boost::tuples::length::value)== - std::tuple_size::value); - - return detail::equal_key_key< - cons_key_tuple, - typename CompositeKey::result_type::key_type, - detail::generic_operator_equal_tuple - >::compare( - detail::make_cons_stdtuple( x ), - y.key, - detail::generic_operator_equal_tuple() ); -} -#endif - -/* < */ - -template -inline bool operator<( - const composite_key_result& x, - const composite_key_result& y) -{ - return detail::compare_key_key< - typename CompositeKey1::result_type::key_type, - typename CompositeKey2::result_type::key_type, - detail::generic_operator_less_tuple - >::compare( - x.key, - y.key, - detail::generic_operator_less_tuple()); -} - -template -inline bool operator<( - const composite_key_result& x, - const Value& y) -{ - return x < boost::make_tuple( boost::cref( y ) ); -} - -template -< - typename CompositeKey, - BOOST_MULTI_INDEX_CK_ENUM_PARAMS( typename Value ) -> -inline bool operator<( - const composite_key_result< CompositeKey >& x, - const tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) >& y ) -{ - typedef boost::tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) > key_tuple; - - return detail::compare_key_key< - typename CompositeKey::result_type::key_type, - key_tuple, - detail::generic_operator_less_tuple - >::compare( - x.key, y, detail::generic_operator_less_tuple() ); -} - -template -< - BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), - typename CompositeKey -> -inline bool operator<( - const tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) >& x, - const composite_key_result< CompositeKey >& y) -{ - typedef boost::tuple key_tuple; - - return detail::compare_key_key< - key_tuple, - typename CompositeKey::result_type::key_type, - detail::generic_operator_less_tuple - >::compare( - x, y.key, detail::generic_operator_less_tuple() ); -} - -#if !defined(BOOST_NO_CXX11_HDR_TUPLE)&&\ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -template -inline bool operator<( - const composite_key_result& x, - const std::tuple& y) -{ - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - return detail::compare_key_key< - typename CompositeKey::result_type::key_type, - cons_key_tuple, - detail::generic_operator_less_tuple - >::compare( - x.key, - detail::make_cons_stdtuple( y ), - detail::generic_operator_less_tuple() ); -} - -template< typename CompositeKey, typename... Values > -inline bool operator<( - const std::tuple< Values... >& x, - const composite_key_result< CompositeKey >& y) -{ - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - return detail::compare_key_key< - cons_key_tuple, - typename CompositeKey::result_type::key_type, - detail::generic_operator_less_tuple - >::compare( - detail::make_cons_stdtuple( x ), - y.key, - detail::generic_operator_less_tuple() ); -} -#endif - -/* rest of comparison operators */ - -#define BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS(t1,t2,a1,a2) \ -template inline bool operator!=(const a1& x,const a2& y) \ -{ \ - return !(x==y); \ -} \ - \ -template inline bool operator>(const a1& x,const a2& y) \ -{ \ - return y inline bool operator>=(const a1& x,const a2& y) \ -{ \ - return !(x inline bool operator<=(const a1& x,const a2& y) \ -{ \ - return !(y, - composite_key_result -) - -BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( - typename CompositeKey, - BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), - composite_key_result, - tuple -) - -BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( - BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), - typename CompositeKey, - tuple, - composite_key_result -) - -#if !defined(BOOST_NO_CXX11_HDR_TUPLE)&&\ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) -BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( - typename CompositeKey, - typename... Values, - composite_key_result, - std::tuple -) - -BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( - typename CompositeKey, - typename... Values, - std::tuple, - composite_key_result -) -#endif - -/* composite_key_equal_to */ - -template -< - BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_TEMPLATE_PARM,Pred) -> -struct composite_key_equal_to: - private tuple -{ -private: - typedef boost::tuple super; - -public: - typedef super key_eq_tuple; - - composite_key_equal_to( - BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_CTOR_ARG,Pred)): - super(BOOST_MULTI_INDEX_CK_ENUM_PARAMS(k)) - {} - - composite_key_equal_to(const key_eq_tuple& x):super(x){} - - const key_eq_tuple& key_eqs()const{return *this;} - key_eq_tuple& key_eqs(){return *this;} - - template - bool operator()( - const composite_key_result & x, - const composite_key_result & y)const - { - typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; - typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value<= - boost::tuples::length::value&& - boost::tuples::length::value== - boost::tuples::length::value); - - return detail::equal_key_key< - typename CompositeKey1::result_type::key_type, - typename CompositeKey2::result_type::key_type, - detail::generic_operator_less_tuple - >::compare( - x.key, - y.key, - key_eqs()); - } - - template - < - typename CompositeKey, - BOOST_MULTI_INDEX_CK_ENUM_PARAMS( typename Value ) - > - bool operator()( - const composite_key_result< CompositeKey >& x, - const tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) >& y)const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef boost::tuple key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value<= - boost::tuples::length::value&& - boost::tuples::length::value== - boost::tuples::length::value); - - return detail::equal_key_key< - typename CompositeKey::result_type::key_type, - key_tuple, - key_eq_tuple - >::compare( x.key, y, key_eqs() ); - } - - template - < - BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), - typename CompositeKey - > - bool operator()( - const tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) >& x, - const composite_key_result< CompositeKey >& y)const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef boost::tuple key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value<= - boost::tuples::length::value&& - boost::tuples::length::value== - boost::tuples::length::value); - - return detail::equal_key_key< - key_tuple, - typename CompositeKey::result_type::key_type, - key_eq_tuple - >::compare( x, y.key, key_eqs() ); - } - -#if !defined(BOOST_NO_CXX11_HDR_TUPLE)&&\ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - template< typename CompositeKey, typename... Values > - bool operator()( - const composite_key_result< CompositeKey >& x, - const std::tuple< Values... >& y )const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value<= - boost::tuples::length::value&& - static_cast(boost::tuples::length::value)== - std::tuple_size::value); - - return detail::equal_key_key< - typename CompositeKey::result_type::key_type, - cons_key_tuple, - key_eq_tuple - >::compare( - x.key, detail::make_cons_stdtuple( y ), key_eqs() ); - } - - template< typename CompositeKey, typename... Values > - bool operator()( - const std::tuple< Values... >& x, - const composite_key_result< CompositeKey >& y)const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - BOOST_STATIC_ASSERT( - std::tuple_size::value<= - static_cast(boost::tuples::length::value)&& - std::tuple_size::value== - static_cast(boost::tuples::length::value)); - - return detail::equal_key_key< - cons_key_tuple, - typename CompositeKey::result_type::key_type, - key_eq_tuple - >::compare( - detail::make_cons_stdtuple( x ), y.key, key_eqs() ); - } -#endif -}; - -/* composite_key_compare */ - -template -< - BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_TEMPLATE_PARM,Compare) -> -struct composite_key_compare: - private tuple -{ -private: - typedef boost::tuple super; - -public: - typedef super key_comp_tuple; - - composite_key_compare( - BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_CTOR_ARG,Compare)): - super(BOOST_MULTI_INDEX_CK_ENUM_PARAMS(k)) - {} - - composite_key_compare(const key_comp_tuple& x):super(x){} - - const key_comp_tuple& key_comps()const{return *this;} - key_comp_tuple& key_comps(){return *this;} - - template - bool operator()( - const composite_key_result & x, - const composite_key_result & y)const - { - typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; - typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value<= - boost::tuples::length::value|| - boost::tuples::length::value<= - boost::tuples::length::value); - - return detail::compare_key_key< - typename CompositeKey1::result_type::key_type, - typename CompositeKey2::result_type::key_type, - key_comp_tuple - >::compare( - x.key, - y.key, - key_comps()); - } - -#if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING) - template - bool operator()( - const composite_key_result& x, - const Value& y)const - { - return operator()(x,boost::make_tuple(boost::cref(y))); - } -#endif - - template - < - typename CompositeKey, - BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value) - > - bool operator()( - const composite_key_result< CompositeKey >& x, - const tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) >& y )const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef boost::tuple key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value<= - boost::tuples::length::value|| - boost::tuples::length::value<= - boost::tuples::length::value); - - return detail::compare_key_key< - typename CompositeKey::result_type::key_type, - key_tuple, - key_comp_tuple - >::compare( x.key, y, key_comps() ); - } - -#if !defined(BOOST_NO_FUNCTION_TEMPLATE_ORDERING) - template - bool operator()( - const Value& x, - const composite_key_result& y)const - { - return operator()(boost::make_tuple(boost::cref(x)),y); - } -#endif - - template - < - BOOST_MULTI_INDEX_CK_ENUM_PARAMS( typename Value ), - typename CompositeKey - > - bool operator()( - const tuple< BOOST_MULTI_INDEX_CK_ENUM_PARAMS( Value ) >& x, - const composite_key_result< CompositeKey >& y )const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef boost::tuple key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value<= - boost::tuples::length::value|| - boost::tuples::length::value<= - boost::tuples::length::value); - - return detail::compare_key_key< - key_tuple, - typename CompositeKey::result_type::key_type, - key_comp_tuple - >::compare( x, y.key, key_comps() ); - } - -#if !defined(BOOST_NO_CXX11_HDR_TUPLE)&&\ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - template< typename CompositeKey, typename... Values > - bool operator()( - const composite_key_result< CompositeKey >& x, - const std::tuple< Values... >& y )const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value<= - boost::tuples::length::value|| - std::tuple_size::value<= - static_cast(boost::tuples::length::value)); - - return detail::compare_key_key< - typename CompositeKey::result_type::key_type, - cons_key_tuple, - key_comp_tuple - >::compare( - x.key, - detail::make_cons_stdtuple(y), - key_comps() ); - } - - template< typename CompositeKey, typename... Values > - bool operator()( - const std::tuple< Values... >& x, - const composite_key_result< CompositeKey >& y )const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - BOOST_STATIC_ASSERT( - std::tuple_size::value<= - static_cast(boost::tuples::length::value)|| - boost::tuples::length::value<= - boost::tuples::length::value); - - return detail::compare_key_key< - cons_key_tuple, - typename CompositeKey::result_type::key_type, - key_comp_tuple - >::compare( - detail::make_cons_stdtuple(x), - y.key, - y.value,key_comps() ); - } -#endif -}; - -/* composite_key_hash */ - -template -< - BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_TEMPLATE_PARM,Hash) -> -struct composite_key_hash: - private tuple -{ -private: - typedef boost::tuple super; - -public: - typedef super key_hasher_tuple; - - composite_key_hash( - BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_CTOR_ARG,Hash)): - super(BOOST_MULTI_INDEX_CK_ENUM_PARAMS(k)) - {} - - composite_key_hash(const key_hasher_tuple& x):super(x){} - - const key_hasher_tuple& key_hash_functions()const{return *this;} - key_hasher_tuple& key_hash_functions(){return *this;} - - template - std::size_t operator()(const composite_key_result & x)const - { - typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; - typedef typename CompositeKey::value_type value_type; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value== - boost::tuples::length::value); - - return detail::hash_ckey< - key_extractor_tuple,value_type, - key_hasher_tuple - >::hash(x.composite_key.key_extractors(),x.value,key_hash_functions()); - } - - template - std::size_t operator()( - const tuple& x)const - { - typedef boost::tuple key_tuple; - - BOOST_STATIC_ASSERT( - boost::tuples::length::value== - boost::tuples::length::value); - - return detail::hash_cval< - key_tuple,key_hasher_tuple - >::hash(x,key_hash_functions()); - } - -#if !defined(BOOST_NO_CXX11_HDR_TUPLE)&&\ - !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) - template - std::size_t operator()(const std::tuple& x)const - { - typedef std::tuple key_tuple; - typedef typename detail::cons_stdtuple_ctor< - key_tuple>::result_type cons_key_tuple; - - BOOST_STATIC_ASSERT( - std::tuple_size::value== - static_cast(boost::tuples::length::value)); - - return detail::hash_cval< - cons_key_tuple,key_hasher_tuple - >::hash(detail::make_cons_stdtuple(x),key_hash_functions()); - } -#endif -}; - -/* Instantiations of the former functors with "natural" basic components: - * composite_key_result_equal_to uses std::equal_to of the values. - * composite_key_result_less uses std::less. - * composite_key_result_greater uses std::greater. - * composite_key_result_hash uses boost::hash. - */ - -#define BOOST_MULTI_INDEX_CK_RESULT_EQUAL_TO_SUPER \ -composite_key_equal_to< \ - BOOST_MULTI_INDEX_CK_ENUM( \ - BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N, \ - /* the argument is a PP list */ \ - (detail::nth_composite_key_equal_to, \ - (BOOST_DEDUCED_TYPENAME CompositeKeyResult::composite_key_type, \ - BOOST_PP_NIL))) \ - > - -template -struct composite_key_result_equal_to: -BOOST_MULTI_INDEX_PRIVATE_IF_USING_DECL_FOR_TEMPL_FUNCTIONS -BOOST_MULTI_INDEX_CK_RESULT_EQUAL_TO_SUPER -{ -private: - typedef BOOST_MULTI_INDEX_CK_RESULT_EQUAL_TO_SUPER super; - -public: - typedef CompositeKeyResult first_argument_type; - typedef first_argument_type second_argument_type; - typedef bool result_type; - - using super::operator(); -}; - -#define BOOST_MULTI_INDEX_CK_RESULT_LESS_SUPER \ -composite_key_compare< \ - BOOST_MULTI_INDEX_CK_ENUM( \ - BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N, \ - /* the argument is a PP list */ \ - (detail::nth_composite_key_less, \ - (BOOST_DEDUCED_TYPENAME CompositeKeyResult::composite_key_type, \ - BOOST_PP_NIL))) \ - > - -template -struct composite_key_result_less: -BOOST_MULTI_INDEX_PRIVATE_IF_USING_DECL_FOR_TEMPL_FUNCTIONS -BOOST_MULTI_INDEX_CK_RESULT_LESS_SUPER -{ -private: - typedef BOOST_MULTI_INDEX_CK_RESULT_LESS_SUPER super; - -public: - typedef CompositeKeyResult first_argument_type; - typedef first_argument_type second_argument_type; - typedef bool result_type; - - using super::operator(); -}; - -#define BOOST_MULTI_INDEX_CK_RESULT_GREATER_SUPER \ -composite_key_compare< \ - BOOST_MULTI_INDEX_CK_ENUM( \ - BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N, \ - /* the argument is a PP list */ \ - (detail::nth_composite_key_greater, \ - (BOOST_DEDUCED_TYPENAME CompositeKeyResult::composite_key_type, \ - BOOST_PP_NIL))) \ - > - -template -struct composite_key_result_greater: -BOOST_MULTI_INDEX_PRIVATE_IF_USING_DECL_FOR_TEMPL_FUNCTIONS -BOOST_MULTI_INDEX_CK_RESULT_GREATER_SUPER -{ -private: - typedef BOOST_MULTI_INDEX_CK_RESULT_GREATER_SUPER super; - -public: - typedef CompositeKeyResult first_argument_type; - typedef first_argument_type second_argument_type; - typedef bool result_type; - - using super::operator(); -}; - -#define BOOST_MULTI_INDEX_CK_RESULT_HASH_SUPER \ -composite_key_hash< \ - BOOST_MULTI_INDEX_CK_ENUM( \ - BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N, \ - /* the argument is a PP list */ \ - (detail::nth_composite_key_hash, \ - (BOOST_DEDUCED_TYPENAME CompositeKeyResult::composite_key_type, \ - BOOST_PP_NIL))) \ - > - -template -struct composite_key_result_hash: -BOOST_MULTI_INDEX_PRIVATE_IF_USING_DECL_FOR_TEMPL_FUNCTIONS -BOOST_MULTI_INDEX_CK_RESULT_HASH_SUPER -{ -private: - typedef BOOST_MULTI_INDEX_CK_RESULT_HASH_SUPER super; - -public: - typedef CompositeKeyResult argument_type; - typedef std::size_t result_type; - - using super::operator(); -}; - -} /* namespace multi_index */ -//* - -template< typename T > struct is_static_length< multi_index::composite_key_result< T > > - : public is_static_length< typename multi_index::composite_key_result< T >::key_type > {}; -/* -template< typename T > -struct slice_packer< multi_index::composite_key_result< T > > -{ - static void pack( PinnableSlice& s , const T& t ) - { - if( is_static_length< typename multi_index::composite_key_result< T >::key_type >::value ) - { - static_packer< multi_index::composite_key_result< T >::key_type >::pack( s, t.key ); - } - else - { - dynamic_packer< multi_index::composite_key_result< T >::key_type >::pack( s, t.key ); - } - } - - static void unpack( PinnableSlice& s , const T& t ) - { - if( is_static_length< typename multi_index::composite_key_result< T >::key_type >::value ) - { - static_packer< multi_index::composite_key_result< T > >::unpack( s, t ); - } - else - { - dynamic_packer< multi_index::composite_key_result< T > >::unpack( s, t ); - } - } -}; -*/ -/* -template< typename T > -inline void pack_to_slice< multi_index::composite_key_result< T > >( PinnableSlice& s, const multi_index::composite_key_result< T >& t ) -{ - -} - -template< typename T > -inline void unpack_from_slice< multi_index::composite_key_result< T > >( const Slice& s, multi_index::composite_key_result< T >& t ) -{ - if( is_static_length< typename multi_index::composite_key_result< T >::key_type >::value ) - { - t = *(multi_index::composite_key_result< T >*)s.data(); - } - else - { - fc::raw::unpack_from_char_array< multi_index::composite_key_result< T > >( s.data(), s.size(), t ); - } -} -//*/ -} /* namespace mira */ - -/* Specializations of std::equal_to, std::less, std::greater and boost::hash - * for composite_key_results enabling interoperation with tuples of values. - */ - -namespace std{ - -template -struct equal_to >: - mira::multi_index::composite_key_result_equal_to< - mira::multi_index::composite_key_result - > -{ -}; - -template -struct less >: - mira::multi_index::composite_key_result_less< - mira::multi_index::composite_key_result - > -{ -}; - -template -struct greater >: - mira::multi_index::composite_key_result_greater< - mira::multi_index::composite_key_result - > -{ -}; - -} /* namespace std */ - -namespace boost{ - -template -struct hash >: - mira::multi_index::composite_key_result_hash< - mira::multi_index::composite_key_result - > -{ -}; - -} /* namespace boost */ - -#undef BOOST_MULTI_INDEX_CK_RESULT_HASH_SUPER -#undef BOOST_MULTI_INDEX_CK_RESULT_GREATER_SUPER -#undef BOOST_MULTI_INDEX_CK_RESULT_LESS_SUPER -#undef BOOST_MULTI_INDEX_CK_RESULT_EQUAL_TO_SUPER -#undef BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS -#undef BOOST_MULTI_INDEX_CK_IDENTITY_ENUM_MACRO -#undef BOOST_MULTI_INDEX_CK_NTH_COMPOSITE_KEY_FUNCTOR -#undef BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N -#undef BOOST_MULTI_INDEX_CK_CTOR_ARG -#undef BOOST_MULTI_INDEX_CK_TEMPLATE_PARM -#undef BOOST_MULTI_INDEX_CK_ENUM_PARAMS -#undef BOOST_MULTI_INDEX_CK_ENUM -#undef BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE - -namespace fc -{ - -class variant; - -template< typename T > -void to_variant( const mira::multi_index::composite_key_result< T >& var, variant& vo ) -{ - to_variant( var.key, vo ); -} - -template< typename T > -void from_variant( const variant& vo, mira::multi_index::composite_key_result< T >& var ) -{ - from_variant( vo, var.key ); -} - -template< typename Cons > struct cons_to_variant; - -template< typename Cons > -struct cons_to_variant_terminal -{ - static void push_variant( const Cons& c, fc::variants& vars ) - { - fc::variant v; - to_variant( c.get_head(), v ); - vars.push_back( v ); - } -}; - -template< typename Cons > -struct cons_to_variant_normal -{ - static void push_variant( const Cons& c, fc::variants& vars ) - { - fc::variant v; - to_variant( c.get_head(), v ); - vars.push_back( v ); - cons_to_variant< typename Cons::tail_type >::push_variant( c.get_tail(), vars ); - } -}; - -template< typename Cons > -struct cons_to_variant : - boost::mpl::if_< - boost::is_same< typename Cons::tail_type, boost::tuples::null_type >, - cons_to_variant_terminal< Cons >, - cons_to_variant_normal< Cons > - >::type -{}; - -template< typename HT, typename TT > void to_variant( const boost::tuples::cons< HT, TT >& var, variant& vo ) -{ - fc::variants v; - cons_to_variant< boost::tuples::cons< HT, TT > >::push_variant( var, v ); - vo = v; -} - -template< typename T > -struct get_typename< mira::multi_index::composite_key_result< T > > -{ - static const char* name() - { - static std::string n = std::string( "mira::multi_index::composite_key_result<" ) - + get_typename< typename T::value_type >::name() - + ">"; - return n.c_str(); - } -}; - -template <> -struct get_typename< boost::tuples::null_type > -{ - static const char* name() - { - static std::string n = std::string( "boost::tuples::null_type" ); - return n.c_str(); - } -}; - -template< typename H, typename T > -struct get_typename< boost::tuples::cons< H, T > > -{ - static const char* name() - { - static std::string n = std::string( "boost::tuples::cons<" ) - + get_typename< H >::name() - + "," - + get_typename< T >::name() - + ">"; - return n.c_str(); - } -}; - -namespace raw -{ - -template< typename Stream, typename T > -void inline pack( Stream& s, const mira::multi_index::composite_key_result< T >& var ) -{ - pack( s, var.key ); -} - -template -void inline unpack( Stream& s, mira::multi_index::composite_key_result< T >& var, uint32_t depth ) -{ - depth++; - unpack( s, var.key, depth + 1 ); -} - - -template< typename Stream > void pack( Stream&, const boost::tuples::null_type ) {} -template< typename Stream > void unpack( Stream& s, boost::tuples::null_type, uint32_t depth ) {} - -template< typename Stream, typename H, typename T > void pack( Stream& s, const boost::tuples::cons< H, T >& var ) -{ - pack( s, var.get_head() ); - pack( s, var.get_tail() ); -} - -template< typename Stream, typename H, typename T > void unpack( Stream& s, boost::tuples::cons< H, T >& var, uint32_t depth ) -{ - depth++; - unpack( s, var.get_head(), depth ); - unpack( s, var.get_tail(), depth ); -} - -} // raw - -} // fc diff --git a/libraries/mira/include/mira/composite_key_fwd.hpp b/libraries/mira/include/mira/composite_key_fwd.hpp deleted file mode 100644 index c7af93ace5..0000000000 --- a/libraries/mira/include/mira/composite_key_fwd.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include -#include - -namespace mira { namespace multi_index { - -template< typename T > struct composite_key_result; - -} } // mira::multi_index - -namespace fc { - -class variant; - -template< typename T > void to_variant( const mira::multi_index::composite_key_result< T >& var, variant& vo ); -template< typename T > void from_variant( const variant& vo, mira::multi_index::composite_key_result< T >& var ); - -//template< typename... Args > void to_variant( const boost::tuples::tuple< Args... >& var, variant& vo ); -//template< typename... Args > void from_variant( const variant& vo, boost::tuples::tuple< Args... >& var ); - -template< typename H, typename T > void to_variant( const boost::tuples::cons< H, T >& var, variant& vo ); -template< typename H, typename T > void from_variant( const variant& vo, boost::tuples::cons< H, T >& var ); - -template< typename T > struct get_typename< mira::multi_index::composite_key_result< T > >; - -template< typename H, typename T > struct get_typename< boost::tuples::cons< H, T > >; - -template<> struct get_typename< boost::tuples::null_type >; - -namespace raw { - -template< typename Stream, typename T > void pack( Stream& s, const mira::multi_index::composite_key_result< T >& var ); -template< typename Stream, typename T > void unpack( Stream& s, mira::multi_index::composite_key_result< T >& var, uint32_t depth = 0 ); - -template< typename Stream > void pack( Stream&, const boost::tuples::null_type ); -template< typename Stream > void unpack( Stream& s, boost::tuples::null_type, uint32_t depth = 0 ); - -template< typename Stream, typename H, typename T > void pack( Stream& s, const boost::tuples::cons< H, T >& var ); -template< typename Stream, typename H, typename T > void unpack( Stream& s, boost::tuples::cons< H, T >& var, uint32_t depth = 0 ); - -} } // fc::raw \ No newline at end of file diff --git a/libraries/mira/include/mira/configuration.hpp b/libraries/mira/include/mira/configuration.hpp deleted file mode 100644 index b0e47e7169..0000000000 --- a/libraries/mira/include/mira/configuration.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mira -{ - -class configuration -{ - configuration() = delete; - static fc::variant_object apply_configuration_overlay( const fc::variant& base, const fc::variant& overlay ); - static fc::variant_object retrieve_active_configuration( const fc::variant_object& obj, std::string type_name ); - static fc::variant_object retrieve_global_configuration( const fc::variant_object& obj ); -public: - static ::rocksdb::Options get_options( const boost::any& cfg, std::string type_name ); - static bool gather_statistics( const boost::any& cfg ); - static size_t get_object_count( const boost::any& cfg ); -}; - -} // mira diff --git a/libraries/mira/include/mira/detail/base_type.hpp b/libraries/mira/include/mira/detail/base_type.hpp deleted file mode 100644 index 2854669ad6..0000000000 --- a/libraries/mira/include/mira/detail/base_type.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright 2003-2013 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include /* keep it first to prevent nasty warns in MSVC */ -#include -#include -#include -#include -#include -#include -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -/* MPL machinery to construct a linear hierarchy of indices out of - * a index list. - */ - -struct index_applier -{ - template - struct apply - { - typedef typename IndexSpecifierMeta::type index_specifier; - typedef typename index_specifier:: - BOOST_NESTED_TEMPLATE index_class::type type; - }; -}; - -template -struct nth_layer -{ - BOOST_STATIC_CONSTANT(int,length=boost::mpl::size::value); - - typedef typename boost::mpl::eval_if_c< - N==length, - boost::mpl::identity >, - boost::mpl::apply2< - index_applier, - boost::mpl::at_c, - nth_layer - > - >::type type; -}; - -template -struct multi_index_base_type:nth_layer<0,Value,IndexSpecifierList,Allocator> -{ - BOOST_STATIC_ASSERT(detail::is_index_list::value); -}; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace boost */ diff --git a/libraries/mira/include/mira/detail/cons_stdtuple.hpp b/libraries/mira/include/mira/detail/cons_stdtuple.hpp deleted file mode 100644 index d8add52a00..0000000000 --- a/libraries/mira/include/mira/detail/cons_stdtuple.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -using boost::multi_index::detail::cons_stdtuple_ctor_terminal; - -template -using cons_stdtuple_ctor_normal = boost::multi_index::detail::cons_stdtuple_ctor_normal< StdTuple, N>; - -template -using cons_stdtuple_ctor = boost::multi_index::detail::cons_stdtuple_ctor< StdTuple, N >; - -template -using cons_stdtuple = boost::multi_index::detail::cons_stdtuple< StdTuple, N >; - -using boost::multi_index::detail::make_cons_stdtuple; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/do_not_copy_elements_tag.hpp b/libraries/mira/include/mira/detail/do_not_copy_elements_tag.hpp deleted file mode 100644 index 12d9957696..0000000000 --- a/libraries/mira/include/mira/detail/do_not_copy_elements_tag.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -using boost::multi_index::detail::do_not_copy_elements_tag; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/duplicates_iterator.hpp b/libraries/mira/include/mira/detail/duplicates_iterator.hpp deleted file mode 100644 index 4abb8a5a4c..0000000000 --- a/libraries/mira/include/mira/detail/duplicates_iterator.hpp +++ /dev/null @@ -1,113 +0,0 @@ -/* Copyright 2003-2013 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include /* keep it first to prevent nasty warns in MSVC */ -#include -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -/* duplicates_operator is given a range of ordered elements and - * passes only over those which are duplicated. - */ - -template -class duplicates_iterator -{ -public: - typedef typename Node::value_type value_type; - typedef std::ptrdiff_t difference_type; - typedef const typename Node::value_type* pointer; - typedef const typename Node::value_type& reference; - typedef std::forward_iterator_tag iterator_category; - - duplicates_iterator(Node* node_,Node* end_,Predicate pred_): - node(node_),begin_chunk(0),end(end_),pred(pred_) - { - advance(); - } - - duplicates_iterator(Node* end_,Predicate pred_): - node(end_),begin_chunk(end_),end(end_),pred(pred_) - { - } - - reference operator*()const - { - return node->value(); - } - - pointer operator->()const - { - return &node->value(); - } - - duplicates_iterator& operator++() - { - Node::increment(node); - sync(); - return *this; - } - - duplicates_iterator operator++(int) - { - duplicates_iterator tmp(*this); - ++(*this); - return tmp; - } - - Node* get_node()const{return node;} - -private: - void sync() - { - if(node!=end&&pred(begin_chunk->value(),node->value()))advance(); - } - - void advance() - { - for(Node* node2=node;node!=end;node=node2){ - Node::increment(node2); - if(node2!=end&&!pred(node->value(),node2->value()))break; - } - begin_chunk=node; - } - - Node* node; - Node* begin_chunk; - Node* end; - Predicate pred; -}; - -template -bool operator==( - const duplicates_iterator& x, - const duplicates_iterator& y) -{ - return x.get_node()==y.get_node(); -} - -template -bool operator!=( - const duplicates_iterator& x, - const duplicates_iterator& y) -{ - return !(x==y); -} - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/has_tag.hpp b/libraries/mira/include/mira/detail/has_tag.hpp deleted file mode 100644 index f3b239b33f..0000000000 --- a/libraries/mira/include/mira/detail/has_tag.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -using boost::multi_index::detail::has_tag; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/index_base.hpp b/libraries/mira/include/mira/detail/index_base.hpp deleted file mode 100644 index 3ed0201ff8..0000000000 --- a/libraries/mira/include/mira/detail/index_base.hpp +++ /dev/null @@ -1,206 +0,0 @@ -/* Copyright 2003-2017 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include /* keep it first to prevent nasty warns in MSVC */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -/* The role of this class is threefold: - * - tops the linear hierarchy of indices. - * - terminates some cascading backbone function calls (insert_, etc.), - * - grants access to the backbone functions of the final - * multi_index_container class (for access restriction reasons, these - * cannot be called directly from the index classes.) - */ - -//struct lvalue_tag{}; -//struct rvalue_tag{}; -//struct emplaced_tag{}; - -template -class index_base -{ -protected: - typedef multi_index_container< - Value,IndexSpecifierList,Allocator> final_type; - typedef boost::tuples::null_type ctor_args_list; - typedef typename std::allocator< Value > final_allocator_type; - typedef boost::mpl::vector0<> index_type_list; - typedef boost::mpl::vector0<> iterator_type_list; - typedef boost::mpl::vector0<> const_iterator_type_list; - - explicit index_base(const ctor_args_list&){} - - typedef Value value_type; - - typedef boost::true_type is_terminal_node; - typedef boost::false_type id_type; - typedef boost::false_type id_from_value; - typedef boost::false_type primary_index_type; - typedef boost::false_type iterator; - - db_ptr _db; - ::rocksdb::WriteBatch _write_buffer; - column_handles _handles; - - static const size_t COLUMN_INDEX = 0; - - index_base( const index_base& other ) : - _db( other._db ), - _write_buffer( other._write_buffer ), - _handles( other._handles ) - {} - - index_base( index_base&& other ) : - _db( std::move( other._db ) ), - _write_buffer( std::move( other._write_buffer ) ), - _handles( std::move( other._handles ) ) - {} - - index_base& operator=( const index_base& rhs ) - { - _db = rhs._db; - _write_buffer = rhs._write_buffer; - _handles = rhs._handles; - - return *this; - } - - index_base& operator=( index_base&& rhs ) - { - _db = std::move( rhs._db ); - _write_buffer = std::move( rhs._write_buffer ); - _handles = std::move( rhs._handles ); - - return *this; - } - - ~index_base() {} - - void flush() {} - - bool insert_rocksdb_( const Value& v ) - { - return true; - } - - void erase_(value_type& x) {} - - void clear_(){} - - template< typename Modifier > - bool modify_( Modifier& mod, value_type& v, std::vector< size_t >& ) - { - mod( v ); - return true; - } - - void populate_column_definitions_( column_definitions& defs )const - { - defs.emplace_back( - ::rocksdb::kDefaultColumnFamilyName, - ::rocksdb::ColumnFamilyOptions() - ); - } - - void cache_first_key() {} - - void commit_first_key_update() {} - - void reset_first_key_update() {} - - void cleanup_column_handles() - { - //for(auto& h : _handles) - // { - // if(h.get() == nullptr) - // continue; - - // auto s = _db->DestroyColumnFamilyHandle(h.get()); - // if(s.ok() == false) - // { - // elog("DestroyColumnFamilyHandle failed with error: ${e}", ("e", s.ToString())); - // } - - // h.reset(); - // } - - _handles.clear(); - } - - void dump_lb_call_counts() {} - - /* access to backbone memfuns of Final class */ - - final_type& final(){return *static_cast(this);} - const final_type& final()const{return *static_cast(this);} - - bool final_empty_()const{return final().empty_();} - std::size_t final_size_()const{return final().size_();} - std::size_t final_max_size_()const{return final().max_size_();} - - - template< BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK > - bool final_emplace_rocksdb_( - BOOST_MULTI_INDEX_FUNCTION_PARAM_PACK) - { - return final().emplace_rocksdb_(BOOST_MULTI_INDEX_FORWARD_PARAM_PACK); - } - - bool final_insert( value_type& v ) - { - return final().insert_( v ); - } - - void final_erase_( value_type& v ) - { - final().erase_( v ); - } - - void final_clear_() { final().clear_(); } - - template< typename Modifier > - bool final_modify_( Modifier& mod, value_type& x ) - { - return final().modify_( mod, x ); - } - - size_t final_get_column_size() - { - return final().get_column_size(); - } -}; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/is_index_list.hpp b/libraries/mira/include/mira/detail/is_index_list.hpp deleted file mode 100644 index a8d6cd3f6a..0000000000 --- a/libraries/mira/include/mira/detail/is_index_list.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -using boost::multi_index::detail::is_index_list; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/no_duplicate_tags.hpp b/libraries/mira/include/mira/detail/no_duplicate_tags.hpp deleted file mode 100644 index 1909260269..0000000000 --- a/libraries/mira/include/mira/detail/no_duplicate_tags.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -using boost::multi_index::detail::duplicate_tag_mark; -using boost::multi_index::detail::duplicate_tag_marker; -using boost::multi_index::detail::no_duplicate_tags; -using boost::multi_index::detail::duplicate_tag_list_marker; -using boost::multi_index::detail::no_duplicate_tags_in_index_list; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/object_cache.hpp b/libraries/mira/include/mira/detail/object_cache.hpp deleted file mode 100644 index 77a83c9695..0000000000 --- a/libraries/mira/include/mira/detail/object_cache.hpp +++ /dev/null @@ -1,446 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mira { namespace multi_index { namespace detail { - -class abstract_multi_index_cache_manager -{ -public: - abstract_multi_index_cache_manager() = default; - virtual ~abstract_multi_index_cache_manager() = default; - - virtual bool purgeable( boost::any v ) = 0; - virtual void purge( boost::any v ) = 0; -}; - -class lru_cache_manager -{ -public: - typedef std::list< - std::pair< - boost::any, - std::shared_ptr< abstract_multi_index_cache_manager > - > - > list_type; - - typedef list_type::iterator iterator_type; - typedef list_type::const_iterator const_iterator_type; - -private: - list_type _lru; - size_t _obj_threshold = 5; - std::mutex _lock; - -public: - iterator_type insert( boost::any v, std::shared_ptr< abstract_multi_index_cache_manager >&& m ) - { - std::lock_guard< std::mutex > lock( _lock ); - return _lru.insert( _lru.begin(), std::make_pair( v, m ) ); - } - - void update( const_iterator_type iter ) - { - std::lock_guard< std::mutex > lock( _lock ); - _lru.splice( _lru.begin(), _lru, iter ); - } - - void remove( iterator_type iter ) - { - std::lock_guard< std::mutex > lock( _lock ); - _lru.erase( iter ); - } - - void set_object_threshold( size_t capacity ) - { - _obj_threshold = capacity; - } - - void adjust_capacity() - { - adjust_capacity( _obj_threshold ); - } - - void adjust_capacity( size_t cap ) - { - static size_t attempts = 0; - if ( _lru.size() > cap ) - { - attempts = 0; - size_t max_attempts = _lru.size() - cap; - auto it = _lru.end(); - do - { - // Prevents an infinite loop in the case - // where everything in the cache is considered - // non-purgeable - if ( attempts++ == max_attempts ) - break; - - --it; - - // If we can purge this item, we do, - // otherwise we move it to the front - // so we don't repeatedly ask if it's - // purgeable - if ( it->second->purgeable( it->first ) ) - it->second->purge( it->first ); - else - update( it ); - - it = _lru.end(); - } while ( _lru.size() > cap ); - } - } -}; - -struct cache_manager -{ - static std::shared_ptr< lru_cache_manager >& get( bool reset = false ) - { - static std::shared_ptr< lru_cache_manager > cache_ptr; - - if( !cache_ptr || reset ) - cache_ptr = std::make_shared< lru_cache_manager >(); - - return cache_ptr; - } - - static void reset() - { - get( true ); - } -}; - -typedef const void* cache_key_type; - -template< typename Value > -struct cache_factory; - -template < typename Value > -class multi_index_cache_manager; - -template < typename Value > -class abstract_index_cache -{ -public: - abstract_index_cache() = default; - virtual ~abstract_index_cache() = default; - - friend class multi_index_cache_manager< Value >; - typedef typename std::shared_ptr< Value > ptr_type; - typedef std::pair< ptr_type, lru_cache_manager::iterator_type > cache_bundle_type; - - virtual ptr_type get( cache_key_type key ) = 0; - virtual void update( cache_key_type key, Value&& v ) = 0; - virtual void update( cache_key_type key, Value&& v, const std::vector< size_t >& modified_indices ) = 0; - virtual bool contains( cache_key_type key ) = 0; - virtual std::mutex& get_lock() = 0; - -protected: - std::shared_ptr< multi_index_cache_manager< Value > > _multi_index_cache_manager; - -private: - virtual void set_multi_index_cache_manager( std::shared_ptr< multi_index_cache_manager< Value > >&& m ) - { - _multi_index_cache_manager = m; - } - - virtual void cache( cache_bundle_type bundle ) = 0; - virtual void invalidate( const Value& v ) = 0; - virtual void invalidate( const Value& v, bool update_cache_manager ) = 0; - virtual void clear() = 0; - virtual void clear( bool update_cache_manager ) = 0; - virtual size_t usage() const = 0; - virtual size_t size() const = 0; -}; - -template < typename Value > -class multi_index_cache_manager : - public abstract_multi_index_cache_manager, - public std::enable_shared_from_this< multi_index_cache_manager< Value > > -{ -public: - multi_index_cache_manager() = default; - ~multi_index_cache_manager() = default; - typedef std::unique_ptr< abstract_index_cache< Value > > index_cache_type; - typedef std::shared_ptr< Value > ptr_type; - typedef std::weak_ptr< Value > manager_ptr_type; - typedef cache_factory< Value > factory_type; - typedef std::pair< ptr_type, lru_cache_manager::iterator_type > cache_bundle_type; - -private: - std::map< size_t, index_cache_type > _index_caches; - std::mutex _lock; - -public: - void set_index_cache( size_t index, index_cache_type&& index_cache ) - { - index_cache->set_multi_index_cache_manager( this->shared_from_this() ); - _index_caches[ index ] = std::move( index_cache ); - } - - virtual bool purgeable( boost::any v ) - { - manager_ptr_type value = boost::any_cast< manager_ptr_type >( v ); - assert( !value.expired() ); - - if ( (size_t)value.use_count() > _index_caches.size() ) - return false; - - return true; - } - - virtual void purge( boost::any v ) - { - manager_ptr_type value = boost::any_cast< manager_ptr_type >( v ); - assert( !value.expired() ); - invalidate( *value.lock() ); - } - - const index_cache_type& get_index_cache( size_t index ) - { - assert( index >= 1 ); - assert( index <= _index_caches.size() ); - return _index_caches[ index ]; - } - - ptr_type cache( const Value& value ) - { - Value v = value; - return cache( std::move( v ) ); - } - - ptr_type cache( Value&& value ) - { - ptr_type p = std::make_shared< Value >( std::move( value ) ); - return cache( p ); - } - - ptr_type cache( ptr_type value ) - { - manager_ptr_type lru_value = value; - auto it = cache_manager::get()->insert( lru_value, this->shared_from_this() ); - - cache_bundle_type bundle = std::make_pair( value, it ); - - for ( auto& c : _index_caches ) - c.second->cache( bundle ); - - return value; - } - - void update( cache_bundle_type bundle, Value&& new_value ) - { - std::vector< size_t > modified_indices; - for ( auto& c : _index_caches ) - modified_indices.push_back( c.first ); - - update( bundle, std::move( new_value ), modified_indices ); - } - - void update( cache_bundle_type bundle, Value&& new_value, const std::vector< size_t >& modified_indices ) - { - // Invalidate the keys based on our old value - for ( auto i : modified_indices ) - _index_caches[ i ]->invalidate( *( bundle.first ) ); - - // Replace the value without changing our pointers - *( bundle.first ) = std::move( new_value ); - - // Generate new keys for each index based on the new value - for ( auto i : modified_indices ) - _index_caches[ i ]->cache( bundle ); - - cache_manager::get()->update( bundle.second ); - } - - void invalidate( const Value& v ) - { - assert( _index_caches.begin() != _index_caches.end() ); - - const auto& last_key = _index_caches.rbegin()->first; - - for ( auto& c : _index_caches ) - { - if ( c.first == last_key ) - c.second->invalidate( v, true ); - else - c.second->invalidate( v ); - } - } - - void clear() - { - assert( _index_caches.begin() != _index_caches.end() ); - - const auto& last_key = _index_caches.rbegin()->first; - - for ( auto& c : _index_caches ) - { - if ( c.first == last_key ) - c.second->clear( true ); - else - c.second->clear(); - } - } - - size_t usage() const - { - // This assumes index 1 is the root index - return _index_caches.find( 1 )->second->usage(); - } - - size_t size() const - { - return _index_caches.find( 1 )->second->size(); - } - - std::mutex& get_lock() - { - return _lock; - } -}; - -template< typename Value, typename Key, typename KeyFromValue > -class index_cache : public abstract_index_cache< Value > -{ -public: - typedef typename std::shared_ptr< Value > ptr_type; - typedef typename std::pair< ptr_type, lru_cache_manager::iterator_type > cache_bundle_type; - -private: - KeyFromValue _get_key; - std::map< Key, cache_bundle_type > _cache; - - const Key key( cache_key_type k ) - { - return *( ( Key* )k ); - } - - virtual void cache( cache_bundle_type bundle ) override final - { - auto key = _get_key( *( bundle.first ) ); - auto r = _cache.insert( std::make_pair( key, bundle ) ); - assert( r.second == true ); - boost::ignore_unused( r ); - } - - virtual void invalidate( const Value& v ) override final - { - auto k = _get_key( v ); - auto n = _cache.erase( k ); - assert( n == 1 ); - boost::ignore_unused( n ); - } - - virtual void invalidate( const Value& v, bool update_cache_manager ) override final - { - auto k = _get_key( v ); - auto it = _cache.find( k ); - assert( it != _cache.end() ); - - if ( update_cache_manager ) - cache_manager::get()->remove( it->second.second ); - - _cache.erase( it ); - } - - virtual void clear() override final - { - _cache.clear(); - } - - virtual void clear( bool update_cache_manager ) override final - { - if ( update_cache_manager ) - { - for ( auto& item : _cache ) - cache_manager::get()->remove( item.second.second ); - } - - clear(); - } - - virtual size_t usage() const override final - { - size_t cache_size = 0; - for( const auto& entry : _cache ) - { - cache_size += fc::raw::pack_size( *(entry.second.first) ); - } - - return cache_size; - } - - virtual size_t size() const override final - { - return _cache.size(); - } - - -public: - index_cache() = default; - virtual ~index_cache() = default; - - virtual void update( cache_key_type k, Value&&v ) override final - { - assert( abstract_index_cache< Value >::_multi_index_cache_manager != nullptr ); - abstract_index_cache< Value >::_multi_index_cache_manager->update( _cache[ key( k ) ], std::move( v ) ); - } - - virtual void update( cache_key_type k, Value&& v, const std::vector< size_t >& modified_indices ) override final - { - assert( abstract_index_cache< Value >::_multi_index_cache_manager != nullptr ); - abstract_index_cache< Value >::_multi_index_cache_manager->update( _cache[ key( k ) ], std::move( v ), modified_indices ); - } - - virtual ptr_type get( cache_key_type k ) override final - { - auto itr = _cache.find( key( k ) ); - if ( itr != _cache.end() ) - { - cache_manager::get()->update( itr->second.second ); - return itr->second.first; - } - return ptr_type(); - } - - virtual bool contains( cache_key_type k ) override final - { - return _cache.find( key( k ) ) != _cache.end(); - } - - virtual std::mutex& get_lock() override final - { - return abstract_index_cache< Value >::_multi_index_cache_manager->get_lock(); - } -}; - -template< typename Value > -struct cache_factory -{ - static std::shared_ptr< multi_index_cache_manager< Value > > get_shared_cache( bool reset = false ) - { - static std::shared_ptr< multi_index_cache_manager< Value > > cache_ptr; - - if( !cache_ptr || reset ) - cache_ptr = std::make_shared< multi_index_cache_manager< Value > >(); - - return cache_ptr; - } - - static void reset() - { - get_shared_cache( true ); - } -}; - -} } } // mira::multi_index::detail diff --git a/libraries/mira/include/mira/detail/ord_index_args.hpp b/libraries/mira/include/mira/detail/ord_index_args.hpp deleted file mode 100644 index 8f2d759aa1..0000000000 --- a/libraries/mira/include/mira/detail/ord_index_args.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2003-2013 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -template -using index_args_default_compare = boost::multi_index::detail::index_args_default_compare< KeyFromValue >; - -template -using ordered_index_args = boost::multi_index::detail::ordered_index_args< Arg1, Arg2, Arg3 >; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/ord_index_impl.hpp b/libraries/mira/include/mira/detail/ord_index_impl.hpp deleted file mode 100644 index 258184d672..0000000000 --- a/libraries/mira/include/mira/detail/ord_index_impl.hpp +++ /dev/null @@ -1,878 +0,0 @@ -/* Copyright 2003-2018 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - * - * The internal implementation of red-black trees is based on that of SGI STL - * stl_tree.h file: - * - * Copyright (c) 1996,1997 - * Silicon Graphics Computer Systems, Inc. - * - * Permission to use, copy, modify, distribute and sell this software - * and its documentation for any purpose is hereby granted without fee, - * provided that the above copyright notice appear in all copies and - * that both that copyright notice and this permission notice appear - * in supporting documentation. Silicon Graphics makes no - * representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * - * Copyright (c) 1994 - * Hewlett-Packard Company - * - * Permission to use, copy, modify, distribute and sell this software - * and its documentation for any purpose is hereby granted without fee, - * provided that the above copyright notice appear in all copies and - * that both that copyright notice and this permission notice appear - * in supporting documentation. Hewlett-Packard Company makes no - * representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - */ - -#pragma once - -#include /* keep it first to prevent nasty warns in MSVC */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) -#include -#endif - -#if !defined(BOOST_MULTI_INDEX_DISABLE_SERIALIZATION) -#include -#include -#include -#include -#endif - -#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) -#define BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT_OF(x) \ - detail::scope_guard BOOST_JOIN(check_invariant_,__LINE__)= \ - detail::make_obj_guard(x,&ordered_index_impl::check_invariant_); \ - BOOST_JOIN(check_invariant_,__LINE__).touch(); -#define BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT \ - BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT_OF(*this) -#else -#define BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT_OF(x) -#define BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT -#endif - -#define ROCKSDB_ITERATOR_PARAM_PACK const_cast< column_handles* >( &_handles ), COLUMN_INDEX, super::_db, *_cache - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -/* ordered_index adds a layer of ordered indexing to a given Super and accepts - * an augmenting policy for optional addition of order statistics. - */ - -/* Most of the implementation of unique and non-unique indices is - * shared. We tell from one another on instantiation time by using - * these tags. - */ - -struct ordered_unique_tag{}; - -template< - typename KeyFromValue,typename Compare, - typename SuperMeta,typename TagList,typename Category,typename AugmentPolicy -> -class ordered_index; - -template< - typename KeyFromValue,typename Compare, - typename SuperMeta,typename TagList,typename Category,typename AugmentPolicy -> -class ordered_index_impl: - BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS SuperMeta::type - -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - ,public safe_mode::safe_container< - ordered_index_impl< - KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy> > -#endif -{ - - typedef typename SuperMeta::type super; - -public: - /* types */ - - typedef typename std::remove_const< typename KeyFromValue::result_type >::type key_type; - typedef typename super::value_type value_type; - typedef KeyFromValue key_from_value; - typedef slice_comparator< - key_type, - Compare > key_compare; - typedef boost::tuple ctor_args; - typedef typename super::final_allocator_type allocator_type; -#ifdef BOOST_NO_CXX11_ALLOCATOR - typedef typename allocator_type::reference reference; - typedef typename allocator_type::const_reference const_reference; -#else - typedef value_type& reference; - typedef const value_type& const_reference; -#endif - - typedef boost::false_type is_terminal_node; - - typedef typename boost::mpl::if_< - typename super::is_terminal_node, - ordered_index_impl< - KeyFromValue, - Compare, - SuperMeta, - TagList, - Category, - AugmentPolicy >, - typename super::primary_index_type >::type primary_index_type; - -//* - typedef typename boost::mpl::if_< - typename super::is_terminal_node, - key_type, - typename super::id_type >::type id_type; -//*/ -// typedef typename primary_index_type::id_type id_type; - -//* - typedef typename boost::mpl::if_< - typename super::is_terminal_node, - KeyFromValue, - typename super::id_from_value >::type id_from_value; -//*/ - -// typedef typename primary_index_type::id_from_value id_from_value; - -/* - typedef object_cache< - value_type, - id_type, - id_from_value > object_cache_type; - - typedef object_cache_factory< - value_type, - id_type, - id_from_value > object_cache_factory_type; -*/ - typedef rocksdb_iterator< - value_type, - key_type, - KeyFromValue, - key_compare, - id_type, - id_from_value > iterator; - - typedef iterator const_iterator; - - typedef typename iterator::cache_type object_cache_type; - typedef typename object_cache_type::factory_type object_cache_factory_type; - - typedef std::size_t size_type; - typedef std::ptrdiff_t difference_type; -#ifdef BOOST_NO_CXX11_ALLOCATOR - typedef typename allocator_type::pointer pointer; - typedef typename allocator_type::const_pointer const_pointer; -#else - typedef std::allocator_traits allocator_traits; - typedef typename allocator_traits::pointer pointer; - typedef typename allocator_traits::const_pointer const_pointer; -#endif - typedef typename - boost::reverse_iterator reverse_iterator; - typedef typename - boost::reverse_iterator const_reverse_iterator; - typedef TagList tag_list; - -protected: - typedef boost::tuples::cons< - ctor_args, - typename super::ctor_args_list> ctor_args_list; - typedef typename boost::mpl::push_front< - typename super::index_type_list, - ordered_index< - KeyFromValue,Compare, - SuperMeta,TagList,Category,AugmentPolicy - > >::type index_type_list; - typedef typename boost::mpl::push_front< - typename super::iterator_type_list, - iterator>::type iterator_type_list; - typedef typename boost::mpl::push_front< - typename super::const_iterator_type_list, - const_iterator>::type const_iterator_type_list; - -protected: -#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) - typedef safe_mode::safe_container< - ordered_index_impl> safe_super; -#endif - - typedef typename boost::call_traits< - value_type>::param_type value_param_type; - typedef typename boost::call_traits< - key_type>::param_type key_param_type; - - /* Needed to avoid commas in BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL - * expansion. - */ - - typedef std::pair< - typename primary_index_type::iterator, - bool > emplace_return_type; - - static const size_t COLUMN_INDEX = super::COLUMN_INDEX + 1; - - std::shared_ptr< object_cache_type > _cache; - column_handles& _handles; - - uint32_t _key_modification_count = 0; - rocksdb::FlushOptions _flush_opts; - - fc::optional< key_type > _first_key; - fc::optional< key_type > _first_key_update; - bool _delete_first_key = false; - -public: - - /* construct/copy/destroy - * Default and copy ctors are in the protected section as indices are - * not supposed to be created on their own. No range ctor either. - * Assignment operators defined at ordered_index rather than here. - */ - - allocator_type get_allocator()const BOOST_NOEXCEPT - { - return this->final().get_allocator(); - } - - /* iterators */ - - iterator begin() BOOST_NOEXCEPT - { - if( _first_key.valid() ) - return make_iterator( *_first_key ); - return iterator::begin( ROCKSDB_ITERATOR_PARAM_PACK ); - } - - const_iterator - begin()const BOOST_NOEXCEPT - { - if( _first_key.valid() ) - return make_iterator( *_first_key ); - return const_iterator::begin( ROCKSDB_ITERATOR_PARAM_PACK ); - } - - iterator - end()BOOST_NOEXCEPT{return iterator::end( ROCKSDB_ITERATOR_PARAM_PACK ); } - const_iterator - end()const BOOST_NOEXCEPT{return const_iterator::end( ROCKSDB_ITERATOR_PARAM_PACK ); } - reverse_iterator - rbegin()BOOST_NOEXCEPT{return boost::make_reverse_iterator(end());} - const_reverse_iterator - rbegin()const BOOST_NOEXCEPT{return boost::make_reverse_iterator(end());} - reverse_iterator - rend()BOOST_NOEXCEPT{return boost::make_reverse_iterator(begin());} - const_reverse_iterator - rend()const BOOST_NOEXCEPT{return boost::make_reverse_iterator(begin());} - const_iterator - cbegin()const BOOST_NOEXCEPT{return begin();} - const_iterator - cend()const BOOST_NOEXCEPT{return end();} - const_reverse_iterator - crbegin()const BOOST_NOEXCEPT{return rbegin();} - const_reverse_iterator - crend()const BOOST_NOEXCEPT{return rend();} - - iterator iterator_to( const value_type& x ) - { - return make_iterator( key( x ) ); - } - - const_iterator iterator_to( const value_type& x )const - { - return make_iterator( key( x ) ); - } - - void flush() - { - super::flush(); - super::_db->Flush( rocksdb::FlushOptions(), &*super::_handles[ COLUMN_INDEX ] ); - } - - /* capacity */ - - bool empty()const BOOST_NOEXCEPT{return this->final_empty_();} - size_type size()const BOOST_NOEXCEPT{return this->final_size_();} - size_type max_size()const BOOST_NOEXCEPT{return this->final_max_size_();} - - /* modifiers */ - - BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL( - emplace_return_type,emplace,emplace_impl) - - BOOST_MULTI_INDEX_OVERLOADS_TO_VARTEMPL_EXTRA_ARG( - iterator,emplace_hint,emplace_hint_impl,iterator,position) - - std::pair insert(const value_type& x) - { - BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; - return this->final_insert_( x ); - } - - iterator erase( iterator position ) - { - BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); - BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; - - value_type v = *position; - ++position; - this->final_erase_( v ); - return position; - } - - template< typename Modifier > - bool modify( iterator position, Modifier mod ) - { - BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR( position ); - BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; - - value_type v = *position; - return this->final_modify_( mod, v ); - } - - template< typename Modifier, typename Rollback > - bool modify( iterator position, Modifier mod, Rollback back_ ) - { - BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR( position ); - BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; - - return this->final_modify_( mod, back_, *position ); - } - - void clear()BOOST_NOEXCEPT - { - BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; - this->final_clear_(); - } - - /* observers */ - - key_from_value key_extractor()const{return key;} - key_compare& key_comp()const{return *comp_;} - - /* set operations */ - - /* Internally, these ops rely on const_iterator being the same - * type as iterator. - */ - - template< typename CompatibleKey > - iterator find( const CompatibleKey& x )const - { - return iterator::find( ROCKSDB_ITERATOR_PARAM_PACK, x ); - } - - template - iterator lower_bound( const CompatibleKey& x )const - { - return iterator::lower_bound( ROCKSDB_ITERATOR_PARAM_PACK, x ); - } - - iterator upper_bound( const key_type& x )const - { - return iterator::upper_bound( ROCKSDB_ITERATOR_PARAM_PACK, x ); - } - - template< typename CompatibleKey > - iterator upper_bound( const CompatibleKey& x )const - { - return iterator::upper_bound( ROCKSDB_ITERATOR_PARAM_PACK, x ); - } - - /* range */ - - template< typename LowerBounder > - std::pair< iterator, iterator > - range( LowerBounder lower, key_type& upper )const - { - return iterator::range( ROCKSDB_ITERATOR_PARAM_PACK, lower, upper ); - } - - template< typename CompatibleKey > - std::pair< iterator, iterator > - equal_range( const CompatibleKey& key )const - { - return iterator::equal_range( ROCKSDB_ITERATOR_PARAM_PACK, key ); - } - -BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: - ordered_index_impl(const ctor_args_list& args_list): - super(args_list.get_tail()), - _cache( object_cache_factory_type::get_shared_cache() ), - _handles( super::_handles ), - key(boost::tuples::get<0>(args_list.get_head())), - comp_(std::make_shared(boost::tuples::get<1>(args_list.get_head()))) - { - empty_initialize(); - _cache->set_index_cache( COLUMN_INDEX, std::make_unique< index_cache< value_type, key_type, key_from_value > >() ); - } - - ordered_index_impl() : - super(), - _cache( object_cache_factory_type::get_shared_cache() ), - _handles( super::_handles ) - { - empty_initialize(); - _cache->set_index_cache( COLUMN_INDEX, std::make_unique< index_cache< value_type, key_type, key_from_value > >() ); - } - - ordered_index_impl( const ordered_index_impl& other ) : - super( other ), - _cache( other._cache ), - _handles( super::_handles ), - _key_modification_count( other._key_modification_count ), - _flush_opts( other._flush_opts ), - _first_key( other._first_key ), - _first_key_update( other._first_key_update ), - _delete_first_key( other._delete_first_key ), - key( other.key ), - comp_( other.comp_ ), - id( other.id ) - {} - - ordered_index_impl( ordered_index_impl&& other ) : - super( std::move( other ) ), - _cache( std::move( other._cache ) ), - _handles( super::_handles ), - _key_modification_count( other._key_modification_count ), - _flush_opts( std::move( other._flush_opts ) ), - _first_key( std::move( other._first_key ) ), - _first_key_update( std::move( other._first_key_update ) ), - _delete_first_key( std::move( other._delete_first_key ) ), - key( std::move( other.key ) ), - comp_( std::move( other.comp_ ) ), - id( std::move( other.id ) ) - {} - - ordered_index_impl& operator=( const ordered_index_impl& rhs ) - { - super::operator=( rhs ); - _cache = rhs._cache; - _handles = super::_handles; - _key_modification_count = rhs._key_modification_count; - _flush_opts = rhs._flush_opts; - _first_key = rhs._first_key; - _first_key_update = rhs._first_key_update; - _delete_first_key = rhs._delete_first_key; - key = rhs.key; - comp_ = rhs.comp_; - id = rhs.id; - - return *this; - } - - ordered_index_impl& operator=( ordered_index_impl&& rhs ) - { - super::operator=( std::move( rhs ) ); - _cache = std::move( rhs._cache ); - _handles = super::_handles; - _key_modification_count = rhs._key_modification_count; - _flush_opts = std::move( rhs._flush_opts ); - _first_key = std::move( rhs._first_key ); - _first_key_update = std::move( rhs._first_key_update ); - _delete_first_key = std::move( rhs._delete_first_key ); - key = std::move( rhs.key ); - comp_ = std::move( rhs.comp_ ); - id = std::move( rhs.id ); - - return *this; - } - - iterator make_iterator( const key_type& key ) - { - return iterator( ROCKSDB_ITERATOR_PARAM_PACK, key ); - } - - iterator make_iterator( const ::rocksdb::Slice& s ) - { - return iterator( ROCKSDB_ITERATOR_PARAM_PACK, s ); - } - - const_iterator make_iterator( const key_type& key )const - { - return const_iterator( ROCKSDB_ITERATOR_PARAM_PACK, key ); - } - - const_iterator make_iterator( const ::rocksdb::Slice& s )const - { - return const_iterator( ROCKSDB_ITERATOR_PARAM_PACK, s ); - } - - bool insert_rocksdb_( const value_type& v ) - { - if( super::insert_rocksdb_( v ) ) - { - ::rocksdb::Status s; - ::rocksdb::PinnableSlice read_buffer; - key_type new_key = key( v ); - ::rocksdb::PinnableSlice key_slice; - pack_to_slice< key_type >( key_slice, new_key ); - - s = super::_db->Get( - ::rocksdb::ReadOptions(), - &*super::_handles[ COLUMN_INDEX ], - key_slice, - &read_buffer ); - - // Key already exists, uniqueness constraint violated - if( s.ok() ) - { - //ilog( "Key ${k} already exists. Object: ${o}", ("k",new_key)("o", v) ); - return false; - } - - ::rocksdb::PinnableSlice value_slice; - - if( COLUMN_INDEX == 1 ) - { - // Insert base case - pack_to_slice( value_slice, v ); - } - else - { - // Insert referential case - pack_to_slice( value_slice, id( v ) ); - } - - s = super::_write_buffer.Put( - &*super::_handles[ COLUMN_INDEX ], - key_slice, - value_slice ); - - if( ( _first_key.valid() && key_comp()( new_key, *_first_key ) ) - || !_first_key.valid() ) - { - _first_key_update = new_key; - } - - return s.ok(); - } - return false; - } - - void erase_( value_type& v ) - { - super::erase_( v ); - - auto old_key = key( v ); - PinnableSlice old_key_slice; - pack_to_slice( old_key_slice, old_key ); - - super::_write_buffer.Delete( - &*super::_handles[ COLUMN_INDEX ], - old_key_slice ); - - if( _first_key.valid() && ( key_comp()( old_key, *_first_key ) == key_comp()( *_first_key, old_key ) ) ) - { - auto new_key_itr = ++find( *_first_key ); - if( new_key_itr != end() ) - { - _first_key_update = key( *new_key_itr ); - } - else - { - _delete_first_key = true; - } - } - } - - void clear_() - { - super::_db->DropColumnFamily( &*super::_handles[ COLUMN_INDEX ] ); - super::clear_(); - empty_initialize(); - } - - template< typename Modifier > - bool modify_( Modifier mod, value_type& v, std::vector< size_t >& modified_indices ) - { - key_type old_key = key( v ); - if( super::modify_( mod, v, modified_indices ) ) - { - ::rocksdb::Status s = ::rocksdb::Status::OK(); - - key_type new_key = key( v ); - PinnableSlice new_key_slice; - - PinnableSlice value_slice; - - if( COLUMN_INDEX == 1 ) - { - // Primary key cannot change - if( new_key != old_key ) - return false; - - pack_to_slice( new_key_slice, new_key ); - pack_to_slice( value_slice, v ); - } - else if( new_key != old_key ) - { - size_t col_index = COLUMN_INDEX; - modified_indices.push_back( col_index ); - - ::rocksdb::PinnableSlice read_buffer; - - pack_to_slice( new_key_slice, new_key ); - - s = super::_db->Get( - ::rocksdb::ReadOptions(), - &*super::_handles[ COLUMN_INDEX ], - new_key_slice, - &read_buffer ); - - // New key already exists, uniqueness constraint violated - if( s.ok() ) - { - ilog( "Key ${k} already exists. Object: ${o}", ("k",new_key)("o", v) ); - return false; - } - - PinnableSlice old_key_slice; - pack_to_slice( old_key_slice, old_key ); - - s = super::_write_buffer.Delete( - &*super::_handles[ COLUMN_INDEX ], - old_key_slice ); - - if( !s.ok() ) return false; - - pack_to_slice( value_slice, id( v ) ); - - ++_key_modification_count; - } - else - { - return true; - } - - s = super::_write_buffer.Put( - &*super::_handles[ COLUMN_INDEX ], - new_key_slice, - value_slice ); - - // If the new key is less than the current first, it will be the new first key - if( _first_key.valid() && key_comp()( new_key, *_first_key ) ) - { - _first_key_update = new_key; - } - // Else if we are updating the current first key AND the new key is different... - else if( _first_key.valid() && key_comp()( *_first_key, new_key ) - && ( key_comp()( old_key, *_first_key ) == key_comp()( *_first_key, old_key ) ) ) - { - // Find the second key (first_key_update) - auto first_key_update_itr = ++find( *_first_key ); - if( first_key_update_itr != end() ) - { - auto first_key_update = key( *first_key_update_itr ); - // If the second key is less than the new key, then the second key is the next first key - if( key_comp()( first_key_update, new_key ) ) - { - _first_key_update = first_key_update; - } - // Otherise the new key is between the first key and the second key and will be the next fist key - else - { - _first_key_update = new_key; - } - } - // If there is no second key, then the new key is the next first key - else - { - _first_key_update = new_key; - } - } - - if( _key_modification_count > 1500 ) - { - super::_db->Flush( _flush_opts, &*super::_handles[ COLUMN_INDEX ] ); - _key_modification_count = 0; - } - - return s.ok(); - } - - return false; - } - - void populate_column_definitions_( column_definitions& defs )const - { - super::populate_column_definitions_( defs ); - // TODO: Clean this up so it outputs the tag name instead of a tempalte type. - // But it is unique, so it works. - std::string tags = boost::core::demangle( typeid( tag_list ).name() ); - //std::cout << COLUMN_INDEX << ':' << tags << std::endl; - defs.emplace_back( - tags, - ::rocksdb::ColumnFamilyOptions() - ); - defs.back().options.comparator = &(*comp_); - } - - void cache_first_key() - { - super::cache_first_key(); - if( !_first_key.valid() ) - { - auto b = iterator::begin( ROCKSDB_ITERATOR_PARAM_PACK ); - if( b != end() ) - { - _first_key = key( *b ); - } - } - } - - void commit_first_key_update() - { - super::commit_first_key_update(); - - if( _first_key_update.valid() ) - { - _first_key = _first_key_update; - _first_key_update.reset(); - } - else if( _delete_first_key ) - { - _first_key.reset(); - _delete_first_key = false; - } - } - - void reset_first_key_update() - { - super::reset_first_key_update(); - _first_key_update.reset(); - _delete_first_key = false; - } - - void dump_lb_call_counts() - { - super::dump_lb_call_counts(); - ilog( boost::core::demangle( typeid( tag_list ).name() ) ); - wdump( (iterator::lb_call_count())(iterator::lb_prev_call_count())(iterator::lb_no_prev_count())(iterator::lb_miss_count()) ); - } - -private: - void empty_initialize() {} - - - /* emplace entry point */ - - template< BOOST_MULTI_INDEX_TEMPLATE_PARAM_PACK > - std::pair< typename primary_index_type::iterator, bool > - emplace_impl( BOOST_MULTI_INDEX_FUNCTION_PARAM_PACK ) - { - BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; - - value_type v( std::forward< Args >(args)... ); - - bool res = this->final_emplace_rocksdb_( v ); - - if ( res ) - { - std::lock_guard< std::mutex > lock( _cache->get_lock() ); - _cache->cache( v ); - } - - return std::pair< typename primary_index_type::iterator, bool >( - res ? primary_index_type::iterator_to( v ) : - primary_index_type::end(), - res - ); - } - -protected: /* for the benefit of AugmentPolicy::augmented_interface */ - key_from_value key; - std::shared_ptr< key_compare > comp_; - id_from_value id; - -#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ - BOOST_WORKAROUND(__MWERKS__,<=0x3003) -#pragma parse_mfunc_templ reset -#endif -}; - -template< - typename KeyFromValue,typename Compare, - typename SuperMeta,typename TagList,typename Category,typename AugmentPolicy -> -class ordered_index: - public AugmentPolicy::template augmented_interface< - ordered_index_impl< - KeyFromValue,Compare,SuperMeta,TagList,Category,AugmentPolicy - > - >::type -{ - typedef typename AugmentPolicy::template - augmented_interface< - ordered_index_impl< - KeyFromValue,Compare, - SuperMeta,TagList,Category,AugmentPolicy - > - >::type super; -public: - typedef typename super::ctor_args_list ctor_args_list; - typedef typename super::allocator_type allocator_type; - typedef typename super::iterator iterator; - - ordered_index() = default; - ordered_index( const ordered_index& ) = default; - ordered_index( ordered_index&& ) = default; - ordered_index& operator =( const ordered_index& ) = default; - ordered_index& operator =( ordered_index&& ) = default; - -protected: - ordered_index( - const ctor_args_list& args_list): - super(args_list){} - - ordered_index(const ordered_index& x,do_not_copy_elements_tag): - super(x,do_not_copy_elements_tag()){}; -}; - -} /* namespace multi_index::detail */ - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/detail/raw_compare.hpp b/libraries/mira/include/mira/detail/raw_compare.hpp deleted file mode 100644 index d1e848b67f..0000000000 --- a/libraries/mira/include/mira/detail/raw_compare.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mira { namespace multi_index { namespace detail { - -template< typename T, typename Stream, typename Compare > -int32_t raw_compare( Stream& s1, Stream& s2, const Compare& c ) -{ - if( s1.size() == s2.size() && memcmp( s1.data(), s2.data(), s1.size() ) == 0 ) return 0; - - T t1; - T t2; - - fc::raw::unpack( s1, t1 ); - fc::raw::unpack( s2, t2 ); - - uint32_t r = c( t1, t2 ); - - if( r ) return -1; - - return 1; -} - -} } } // mira::multi_index::detail diff --git a/libraries/mira/include/mira/detail/rocksdb_base.hpp b/libraries/mira/include/mira/detail/rocksdb_base.hpp deleted file mode 100644 index f7edeb8133..0000000000 --- a/libraries/mira/include/mira/detail/rocksdb_base.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -namespace mira { namespace multi_index { namespace detail { - -using ::rocksdb::ColumnFamilyDescriptor; - -class rocksdb_base -{ - - protected: - std::vector< ColumnFamilyDescriptor > _column_defs; - -}; - -} } } // mira::multi_index::detail diff --git a/libraries/mira/include/mira/detail/rocksdb_iterator.hpp b/libraries/mira/include/mira/detail/rocksdb_iterator.hpp deleted file mode 100644 index 3750917ddf..0000000000 --- a/libraries/mira/include/mira/detail/rocksdb_iterator.hpp +++ /dev/null @@ -1,738 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -namespace mira { namespace multi_index { namespace detail { - -template< typename Value, typename Key, typename KeyFromValue, - typename KeyCompare, typename ID, typename IDFromValue > -struct rocksdb_iterator : - public boost::bidirectional_iterator_helper< - rocksdb_iterator< Value, Key, KeyFromValue, KeyCompare, ID, IDFromValue >, - Value, - std::size_t, - const Value*, - const Value& > -{ - typedef Key key_type; - typedef Value value_type; - typedef typename std::shared_ptr< value_type > value_ptr; - typedef multi_index_cache_manager< value_type > cache_type; - -private: - column_handles* _handles; - size_t _index = 0; - - std::unique_ptr< ::rocksdb::Iterator > _iter; - std::shared_ptr< ::rocksdb::ManagedSnapshot > _snapshot; - ::rocksdb::ReadOptions _opts; - db_ptr _db; - - cache_type* _cache = nullptr; - IDFromValue _get_id; - - KeyCompare _compare; - - std::shared_ptr< Value > _cache_value; - - // The following declarations exist solely for the iterator to have a default constructor - static cache_type default_cache; - -public: - - static uint64_t& lb_call_count() - { - static uint64_t count = 0; - return count; - } - - static uint64_t& lb_miss_count() - { - static uint64_t count = 0; - return count; - } - - static uint64_t& lb_prev_call_count() - { - static uint64_t count = 0; - return count; - } - - static uint64_t& lb_no_prev_count() - { - static uint64_t count = 0; - return count; - } - - rocksdb_iterator() {} - - rocksdb_iterator( rocksdb_iterator& other ) : - _handles( other._handles ), - _index( other._index ), - _snapshot( other._snapshot ), - _db( other._db ), - _cache( other._cache ), - _cache_value( other._cache_value ) - { - if ( other._iter ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - - if( other._iter->Valid() ) - _iter->Seek( other._iter->key() ); - } - } - - rocksdb_iterator( const rocksdb_iterator& other ) : - _handles( other._handles ), - _index( other._index ), - _snapshot( other._snapshot ), - _db( other._db ), - _cache( other._cache ), - _cache_value( other._cache_value ) - { - if ( other._iter ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - - if( other._iter->Valid() ) - _iter->Seek( other._iter->key() ); - } - } - - rocksdb_iterator( rocksdb_iterator&& other ) : - _handles( other._handles ), - _index( other._index ), - _iter( std::move( other._iter ) ), - _snapshot( other._snapshot ), - _db( other._db ), - _cache( other._cache ), - _cache_value( other._cache_value ) - { - //_opts.snapshot = _snapshot->snapshot(); - other._snapshot.reset(); - other._db.reset(); - } - - rocksdb_iterator( column_handles* handles, size_t index, db_ptr db, cache_type& cache ) : - _handles( handles ), - _index( index ), - _db( db ), - _cache( &cache ) - { - // Not sure the implicit move constuctor for ManageSnapshot isn't going to release the snapshot... - //_snapshot = std::make_shared< ::rocksdb::ManagedSnapshot >( &(*_db) ); - //_opts.snapshot = _snapshot->snapshot(); - //_iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - } - rocksdb_iterator( std::shared_ptr< Value >& cache_value, column_handles* handles, size_t index, db_ptr db, cache_type& cache ) : - _handles( handles ), - _index( index ), - _db( db ), - _cache( &cache ), - _cache_value( cache_value ) - { - } - - rocksdb_iterator( std::shared_ptr< Value >& cache_value, column_handles* handles, size_t index, db_ptr db, cache_type& cache, std::unique_ptr< ::rocksdb::Iterator > iter ) : - _handles( handles ), - _index( index ), - _iter( std::move( iter ) ), - _db( db ), - _cache( &cache ), - _cache_value( cache_value ) - { - } - - rocksdb_iterator( column_handles* handles, size_t index, db_ptr db, cache_type& cache, const Key& k ) : - _handles( handles ), - _index( index ), - _db( db ), - _cache( &cache ) - { - key_type* id = (key_type*)&k; - std::lock_guard< std::mutex > lock( _cache->get_index_cache( _index )->get_lock() ); - _cache_value = cache.get_index_cache( index )->get( (void*)id ); - if ( _cache_value == nullptr ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - - PinnableSlice key_slice; - pack_to_slice( key_slice, k ); - - _iter->Seek( key_slice ); - - if( !_iter->status().ok() ) - { - std::cout << std::string( _iter->status().getState() ) << std::endl; - } - - assert( _iter->status().ok() && _iter->Valid() ); - } - } - - rocksdb_iterator( column_handles* handles, size_t index, db_ptr db, cache_type& cache, const ::rocksdb::Slice& s ) : - _handles( handles ), - _index( index ), - _db( db ), - _cache( &cache ) - { - Key k; - unpack_from_slice( s, k ); - key_type* id = (key_type*)&k; - std::lock_guard< std::mutex > lock( _cache->get_index_cache( _index )->get_lock() ); - _cache_value = cache.get_index_cache( index )->get( (void*)id ); - if ( _cache_value == nullptr ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - _iter->Seek( s ); - - assert( _iter->status().ok() && _iter->Valid() ); - } - } - - const value_type& operator*() - { - BOOST_ASSERT( valid() ); - value_ptr ptr; - - if ( _cache_value != nullptr ) - { - ptr = _cache_value; - } - else - { - key_type key; - - unpack_from_slice( _iter->key(), key ); - std::lock_guard< std::mutex > lock( _cache->get_index_cache( _index )->get_lock() ); - ptr = _cache->get_index_cache( _index )->get( (void*)&key ); - - if ( !ptr ) - { - if ( _index == ID_INDEX ) - { - // We are iterating on the primary key, so there is no indirection - ::rocksdb::Slice value_slice = _iter->value(); - ptr = std::make_shared< value_type >(); - unpack_from_slice( value_slice, *ptr ); - ptr = _cache->cache( std::move( *ptr ) ); - } - else - { - ::rocksdb::PinnableSlice value_slice; - auto s = _db->Get( _opts, &*(*_handles)[ ID_INDEX ], _iter->value(), &value_slice ); - assert( s.ok() ); - - ptr = std::make_shared< value_type >(); - unpack_from_slice( value_slice, *ptr ); - ptr = _cache->cache( std::move( *ptr ) ); - } - } - - _cache_value = ptr; - } - - return (*ptr); - } - - const value_type* operator->() - { - return &(**this); - } - - rocksdb_iterator& operator++() - { - static KeyFromValue key_from_value = KeyFromValue(); - static KeyCompare compare = KeyCompare(); - //BOOST_ASSERT( valid() ); - if( !valid() ) - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - - if ( _cache_value != nullptr ) - { - Key key = key_from_value( *_cache_value ); - _cache_value.reset(); - - if ( _iter == nullptr ) - { - ::rocksdb::PinnableSlice slice; - pack_to_slice( slice, key ); - - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - _iter->Seek( slice ); - - if( _iter->Valid() ) - { - Key found_key; - unpack_from_slice( _iter->key(), found_key ); - - if( compare( found_key, key ) != compare( key, found_key ) ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - return *this; - } - } - else - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - return *this; - } - } - } - - _iter->Next(); - assert( _iter->status().ok() ); - return *this; - } - - rocksdb_iterator operator++(int)const - { - //BOOST_ASSERT( valid() ); - rocksdb_iterator new_itr( *this ); - ++(*this); - return new_itr; - } - - rocksdb_iterator& operator--() - { - static KeyFromValue key_from_value = KeyFromValue(); - if( !valid() ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - _iter->SeekToLast(); - } - else - { - if ( _cache_value != nullptr ) - { - Key key = key_from_value( *_cache_value ); - _cache_value.reset(); - - if ( _iter == nullptr ) - { - ::rocksdb::PinnableSlice slice; - pack_to_slice( slice, key ); - - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - _iter->Seek( slice ); - - if( _iter->Valid() ) - { - ::rocksdb::Slice found_key = _iter->key(); - if( memcmp( slice.data(), found_key.data(), std::min( slice.size(), found_key.size() ) ) != 0 ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - return *this; - } - } - else - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - return *this; - } - } - } - _iter->Prev(); - } - - assert( _iter->status().ok() ); - return *this; - } - - rocksdb_iterator operator--(int)const - { - rocksdb_iterator new_itr( *this ); - --(*this); - return new_itr; - } - - bool valid()const - { - return ( _cache_value != nullptr ) || ( _iter && _iter->Valid() ); - } - - bool unchecked()const { return false; } - - bool equals( const rocksdb_iterator& other )const - { - static KeyFromValue key_from_value = KeyFromValue(); - if( valid() && other.valid() ) - { - Key this_key, other_key; - - if ( _cache_value != nullptr ) - this_key = key_from_value( *_cache_value ); - else - unpack_from_slice( _iter->key(), this_key ); - - if ( other._cache_value != nullptr ) - other_key = key_from_value( *other._cache_value ); - else - unpack_from_slice( other._iter->key(), other_key ); - - return _compare( this_key, other_key ) == _compare( other_key, this_key ); - } - - return valid() == other.valid(); - } - - rocksdb_iterator& operator=( const rocksdb_iterator& other ) - { - _handles = other._handles; - _index = other._index; - _snapshot = other._snapshot; - _db = other._db; - _cache = other._cache; - _cache_value = other._cache_value; - - if ( other._iter ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - - if( other._iter->Valid() ) - _iter->Seek( other._iter->key() ); - } - - return *this; - } - - rocksdb_iterator& operator=( rocksdb_iterator& other ) - { - _handles = other._handles; - _index = other._index; - _snapshot = other._snapshot; - _db = other._db; - _cache = other._cache; - _cache_value = other._cache_value; - - if ( other._iter ) - { - _iter.reset( _db->NewIterator( _opts, &*(*_handles)[ _index ] ) ); - - if( other._iter->Valid() ) - _iter->Seek( other._iter->key() ); - } - - return *this; - } - - rocksdb_iterator& operator=( rocksdb_iterator&& other ) - { - _handles = other._handles; - _index = other._index; - _snapshot = other._snapshot; - _db = other._db; - _cache = other._cache; - _cache_value = other._cache_value; - - _iter = std::move( other._iter ); - - return *this; - } - - - static rocksdb_iterator begin( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache ) - { - rocksdb_iterator itr( handles, index, db, cache ); - //itr._opts.readahead_size = 4 << 10; // 4K - itr._iter.reset( db->NewIterator( itr._opts, &*(*handles)[ index ] ) ); - itr._iter->SeekToFirst(); - return itr; - } - - static rocksdb_iterator end( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache ) - { - return rocksdb_iterator( handles, index, db, cache ); - } - - template< typename CompatibleKey > - static rocksdb_iterator find( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache, - const CompatibleKey& k ) - { - static KeyCompare compare = KeyCompare(); - - auto key = Key( k ); - std::lock_guard< std::mutex > lock( cache.get_index_cache( index )->get_lock() ); - auto cache_value = cache.get_index_cache( index )->get( (void*)&key ); - if ( cache_value != nullptr ) - { - return rocksdb_iterator( cache_value, handles, index, db, cache ); - } - - rocksdb_iterator itr( handles, index, db, cache ); - itr._iter.reset( db->NewIterator( itr._opts, &*(*handles)[ index ] ) ); - - PinnableSlice key_slice; - pack_to_slice( key_slice, key ); - - itr._iter->Seek( key_slice ); - - if( itr.valid() ) - { - Key found_key; - unpack_from_slice( itr._iter->key(), found_key ); - - if( compare( k, found_key ) != compare( found_key, k ) ) - { - itr._iter.reset( itr._db->NewIterator( itr._opts, &*(*itr._handles)[ itr._index ] ) ); - } - } - else - { - itr._iter.reset( itr._db->NewIterator( itr._opts, &*(*itr._handles)[ itr._index ] ) ); - } - - return itr; - } - - static rocksdb_iterator find( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache, - const Key& k ) - { - static KeyCompare compare = KeyCompare(); - - key_type* id = (key_type*)&k; - std::lock_guard< std::mutex > lock( cache.get_index_cache( index )->get_lock() ); - auto cache_value = cache.get_index_cache( index )->get( (void*)id ); - if ( cache_value != nullptr ) - { - return rocksdb_iterator( cache_value, handles, index, db, cache ); - } - - rocksdb_iterator itr( handles, index, db, cache ); - itr._iter.reset( db->NewIterator( itr._opts, &*(*handles)[ index ] ) ); - - PinnableSlice key_slice; - pack_to_slice( key_slice, k ); - itr._iter->Seek( key_slice ); - - if( itr.valid() ) - { - Key found_key; - unpack_from_slice( itr._iter->key(), found_key ); - - if( compare( k, found_key ) != compare( found_key, k ) ) - { - itr._iter.reset( itr._db->NewIterator( itr._opts, &*(*itr._handles)[ itr._index ] ) ); - } - } - else - { - itr._iter.reset( itr._db->NewIterator( itr._opts, &*(*itr._handles)[ itr._index ] ) ); - } - - return itr; - } - - static rocksdb_iterator lower_bound( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache, - const Key& k ) - { - key_type* id = (key_type*)&k; - std::lock_guard< std::mutex > lock( cache.get_index_cache( index )->get_lock() ); - auto cache_value = cache.get_index_cache( index )->get( (void*)id ); - if ( cache_value != nullptr ) - { - return rocksdb_iterator( cache_value, handles, index, db, cache ); - } - - rocksdb_iterator itr( handles, index, db, cache ); - itr._iter.reset( db->NewIterator( itr._opts, &*(*handles)[ index ] ) ); - - PinnableSlice key_slice; - pack_to_slice( key_slice, k ); - itr._iter->Seek( key_slice ); - - return itr; - } - - template< typename CompatibleKey > - static rocksdb_iterator lower_bound( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache, - const CompatibleKey& k ) - { - static KeyCompare compare = KeyCompare(); - lb_call_count()++; - rocksdb_iterator itr( handles, index, db, cache ); - itr._iter.reset( db->NewIterator( itr._opts, &*(*handles)[ index ] ) ); - - PinnableSlice key_slice; - pack_to_slice( key_slice, Key( k ) ); - - itr._iter->Seek( key_slice ); - - if( itr.valid() ) - { - Key itr_key; - unpack_from_slice( itr._iter->key(), itr_key ); - - //if( !key_equals( itr_key, k, compare ) ) - if( !is_well_ordered< KeyCompare, true >::value && !compare( itr_key, k ) ) - { - rocksdb_iterator prev( handles, index, db, cache ); - do - { - prev = itr--; - lb_prev_call_count()++; - if( !itr.valid() ) return prev; - - unpack_from_slice( itr._iter->key(), itr_key ); - }while( !compare( itr_key, k ) ); - - return prev; - } - else - { - lb_no_prev_count()++; - } - } - else - { - lb_miss_count()++; - } - - return itr; - } -/* - static rocksdb_iterator upper_bound( - const column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache, - const Key& k ) - { - rocksdb_iterator itr( handles, index, db, cache ); - itr._iter.reset( db->NewIterator( itr._opts, &*(*handles)[ index ] ) ); - - PinnableSlice key_slice; - pack_to_slice( key_slice, k ); - - itr._iter->SeekForPrev( key_slice ); - - if( itr.valid() ) - { - itr._iter->Next(); - } - - return itr; - } -*/ - template< typename CompatibleKey > - static rocksdb_iterator upper_bound( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache, - const CompatibleKey& k ) - { - static KeyCompare compare = KeyCompare(); - rocksdb_iterator itr( handles, index, db, cache ); - //itr._opts.readahead_size = 4 << 10; // 4K - itr._iter.reset( db->NewIterator( itr._opts, &*(*handles)[ index ] ) ); - - auto key = Key( k ); - PinnableSlice key_slice; - pack_to_slice( key_slice, key ); - - itr._iter->Seek( key_slice ); - - if( itr.valid() ) - { - Key itr_key; - unpack_from_slice( itr._iter->key(), itr_key ); - - while( !compare( k, itr_key ) ) - { - ++itr; - if( !itr.valid() ) return itr; - - unpack_from_slice( itr._iter->key(), itr_key ); - } - } - - return itr; - } - - template< typename LowerBoundType, typename UpperBoundType > - static std::pair< rocksdb_iterator, rocksdb_iterator > range( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache, - const LowerBoundType& lower, - const UpperBoundType& upper ) - { - return std::make_pair< rocksdb_iterator, rocksdb_iterator >( - lower_bound( handles, index, db, cache, lower ), - upper_bound( handles, index, db, cache, upper ) - ); - } - - template< typename CompatibleKey > - static std::pair< rocksdb_iterator, rocksdb_iterator > equal_range( - column_handles* handles, - size_t index, - db_ptr db, - cache_type& cache, - const CompatibleKey& k ) - { - return std::make_pair< rocksdb_iterator, rocksdb_iterator >( - lower_bound( handles, index, db, cache, k ), - upper_bound( handles, index, db, cache, k ) - ); - } -}; - -template< typename Value, typename Key, typename KeyFromValue, - typename KeyCompare, typename ID, typename IDFromValue > -bool operator==( - const rocksdb_iterator< Value, Key, KeyFromValue, KeyCompare, ID, IDFromValue >& x, - const rocksdb_iterator< Value, Key, KeyFromValue, KeyCompare, ID, IDFromValue >& y) -{ - return x.equals( y ); -} - -template< typename Value, typename Key, typename KeyFromValue, - typename KeyCompare, typename ID, typename IDFromValue > -bool operator!=( - const rocksdb_iterator< Value, Key, KeyFromValue, KeyCompare, ID, IDFromValue >& x, - const rocksdb_iterator< Value, Key, KeyFromValue, KeyCompare, ID, IDFromValue >& y) -{ - return !( x == y ); -} - - -} } } // mira::multi_index::detail diff --git a/libraries/mira/include/mira/detail/slice_compare.hpp b/libraries/mira/include/mira/detail/slice_compare.hpp deleted file mode 100644 index 2f17897790..0000000000 --- a/libraries/mira/include/mira/detail/slice_compare.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include - -#include - -namespace mira { namespace multi_index { namespace detail { - -template< typename Key, typename CompareType > -struct abstract_slice_comparator : ::rocksdb::Comparator, CompareType -{ - abstract_slice_comparator() {} - - virtual const char* Name() const override final - { - static const std::string name = boost::core::demangle(typeid(this).name()); - return name.c_str(); - } - - virtual void FindShortestSeparator( std::string* start, const ::rocksdb::Slice& limit ) const override final - { - /// Nothing to do. - } - - virtual void FindShortSuccessor( std::string* key ) const override final - { - /// Nothing to do. - } -}; - -template< typename Key, typename CompareType > -struct slice_comparator final : abstract_slice_comparator< Key, CompareType > -{ - slice_comparator() : abstract_slice_comparator< Key, CompareType >() - {} - - virtual int Compare( const ::rocksdb::Slice& x, const ::rocksdb::Slice& y ) const override - { - Key x_key; unpack_from_slice( x, x_key ); - Key y_key; unpack_from_slice( y, y_key ); - - int r = (*this)( x_key, y_key ); - - if( r ) return -1; - - r = (*this)( y_key, x_key ); - - if( r ) return 1; - - return 0; - } - - virtual bool Equal( const ::rocksdb::Slice& x, const ::rocksdb::Slice& y ) const override - { - if( x.size() != y.size() ) return false; - Key x_key; unpack_from_slice( x, x_key ); - Key y_key; unpack_from_slice( y, y_key ); - return (*this)( x_key, y_key ) == (*this)( y_key, x_key ); - } -}; - -// TODO: Primitive Types can implement custom comparators that understand the independent format - -} } } // mira::multi_index::detail diff --git a/libraries/mira/include/mira/identity.hpp b/libraries/mira/include/mira/identity.hpp deleted file mode 100644 index 5cdf0eccc0..0000000000 --- a/libraries/mira/include/mira/identity.hpp +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright 2003-2015 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -template< typename Type > -using const_identity_base = boost::multi_index::detail::const_identity_base< Type >; - -template< typename Type > -using non_const_identity_base = boost::multi_index::detail::non_const_identity_base< Type >; - -} /* namespace multi_index::detail */ - -template< class Type > -using identity = boost::multi_index::identity< Type >; - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/index_adapter.hpp b/libraries/mira/include/mira/index_adapter.hpp deleted file mode 100644 index 189bbcd422..0000000000 --- a/libraries/mira/include/mira/index_adapter.hpp +++ /dev/null @@ -1,584 +0,0 @@ -#pragma once -#include -#include - -namespace mira { - -enum index_type -{ - mira, - bmic -}; - -template< typename MultiIndexAdapterType, typename IndexedBy > -struct index_adapter -{ - typedef typename MultiIndexAdapterType::value_type value_type; - typedef typename std::remove_reference< decltype( - (((typename MultiIndexAdapterType::mira_type*)nullptr)->template get()) ) >::type mira_type; - typedef typename std::remove_reference< decltype( - (((typename MultiIndexAdapterType::bmic_type*)nullptr)->template get()) ) >::type bmic_type; - - typedef decltype( (((mira_type*)nullptr)->begin()) ) mira_iter_type; - typedef decltype( (((bmic_type*)nullptr)->begin()) ) bmic_iter_type; - - typedef iterator_adapter< - value_type, - mira_iter_type, - bmic_iter_type > iter_type; - typedef boost::reverse_iterator< iter_type > rev_iter_type; - - typedef boost::variant< mira_type*, bmic_type* > index_variant; - - private: - index_adapter() {} - - struct erase_visitor : public boost::static_visitor< iter_type > - { - iter_type& _pos; - - erase_visitor( iter_type& pos ) : _pos( pos ) {} - - iter_type operator()( mira_type* idx_ptr ) const - { - return iter_type::create( idx_ptr->erase( _pos.template get< mira_iter_type >() ) ); - } - - iter_type operator()( bmic_type* idx_ptr ) const - { - return iter_type::create( idx_ptr->erase( _pos.template get< bmic_iter_type >() ) ); - } - }; - - public: - index_adapter( const mira_type& mira_index ) - { - _index = const_cast< mira_type* >( &mira_index ); - } - - index_adapter( const bmic_type& bmic_index ) - { - _index = const_cast< bmic_type* >( &bmic_index ); - } - - index_adapter( const index_adapter& other ) : - _index( other._index ) - {} - - index_adapter( index_adapter&& other ) : - _index( std::move( other._index ) ) - {} - - iter_type erase( iter_type position ) - { - return boost::apply_visitor( erase_visitor( position ), _index ); - } - - iter_type iterator_to( const value_type& v )const - { - return boost::apply_visitor( - [&]( auto* index ){ return iter_type::create( index->iterator_to( v ) ); }, - _index - ); - } - - template< typename CompatibleKey > - iter_type find( const CompatibleKey& k )const - { - return boost::apply_visitor( - [&k]( auto* index ){ return iter_type::create( index->find( k ) ); }, - _index - ); - } - - template< typename CompatibleKey > - iter_type lower_bound( const CompatibleKey& k )const - { - return boost::apply_visitor( - [&k]( auto* index ){ return iter_type::create( index->lower_bound( k ) ); }, - _index - ); - } - - template< typename CompatibleKey > - iter_type upper_bound( const CompatibleKey& k )const - { - return boost::apply_visitor( - [&k]( auto* index ){ return iter_type::create( index->upper_bound( k ) ); }, - _index - ); - } - - template< typename CompatibleKey > - std::pair< iter_type, iter_type > equal_range( const CompatibleKey& k )const - { - return boost::apply_visitor( - [&k]( auto* index ) - { - auto result = index->equal_range( k ); - return std::pair< iter_type, iter_type >( - iter_type::create( std::move( result.first ) ), - iter_type::create( std::move( result.second ) ) - ); - }, - _index - ); - } - - iter_type begin()const - { - return boost::apply_visitor( - []( auto* index ){ return iter_type::create( index->begin() ); }, - _index - ); - } - - iter_type end()const - { - return boost::apply_visitor( - []( auto* index ){ return iter_type::create( index->end() ); }, - _index - ); - } - - rev_iter_type rbegin()const - { - return boost::make_reverse_iterator( end() ); - } - - rev_iter_type rend()const - { - return boost::make_reverse_iterator( begin() ); - } - - bool empty()const - { - return boost::apply_visitor( - []( auto* index ){ return index->empty(); }, - _index - ); - } - - size_t size()const - { - return boost::apply_visitor( - []( auto* index ){ return index->size(); }, - _index - ); - } - - private: - index_variant _index; -}; - -template< typename Arg1, typename Arg2, typename Arg3 > -struct multi_index_adapter -{ - typedef Arg1 value_type; - typedef typename index_converter< multi_index::multi_index_container< Arg1, Arg2, Arg3 > >::mira_type mira_type; - typedef typename mira_type::primary_iterator mira_iter_type; - typedef typename index_converter< multi_index::multi_index_container< Arg1, Arg2, Arg3 > >::bmic_type bmic_type; - typedef typename bmic_type::iterator bmic_iter_type; - typedef typename value_type::id_type id_type; - - typedef iterator_adapter< - value_type, - mira_iter_type, - bmic_iter_type > iter_type; - typedef boost::reverse_iterator< iter_type > rev_iter_type; - - typedef typename bmic_type::allocator_type allocator_type; - - typedef boost::variant< - mira_type, - bmic_type > index_variant; - - multi_index_adapter() - { - _index = mira_type(); - } - - multi_index_adapter( allocator_type& a ) - { - _index = mira_type(); - } - - multi_index_adapter( index_type type ) : - _type( type ) - { - switch( _type ) - { - case mira: - _index = mira_type(); - break; - case bmic: - _index = bmic_type(); - break; - } - } - - multi_index_adapter( index_type type, allocator_type& a ) : - _type( type ) - { - switch( _type ) - { - case mira: - _index = mira_type(); - break; - case bmic: - _index = bmic_type( a ); - break; - } - } - - ~multi_index_adapter() {} - - template< typename IndexedBy > - index_adapter< multi_index_adapter< Arg1, Arg2, Arg3 >, IndexedBy > get() - { - return boost::apply_visitor( - []( auto& index ) - { - return index_adapter< multi_index_adapter< Arg1, Arg2, Arg3 >, IndexedBy >( - index.template get< IndexedBy >() - ); - }, - _index - ); - } - - template< typename IndexedBy > - index_adapter< multi_index_adapter< Arg1, Arg2, Arg3 >, IndexedBy > mutable_get() - { - return boost::apply_visitor( - []( auto& index ) - { - return index_adapter< multi_index_adapter< Arg1, Arg2, Arg3 >, IndexedBy >( - index.template get< IndexedBy >() - ); - }, - _index - ); - } - - template< typename IndexedBy > - const index_adapter< multi_index_adapter< Arg1, Arg2, Arg3 >, IndexedBy > get()const - { - return boost::apply_visitor( - []( const auto& index ) - { - return index_adapter< multi_index_adapter< Arg1, Arg2, Arg3 >, IndexedBy >( - index.template get< IndexedBy >() - ); - }, - _index - ); - } - - void set_index_type( index_type type, const boost::filesystem::path& p, const boost::any& cfg ) - { - if( type == _type ) return; - - index_variant new_index; - - auto id = next_id(); - auto rev = revision(); - - { - auto first = begin(); - auto last = end(); - - switch( type ) - { - case mira: - new_index = std::move( mira_type( first, last, p, cfg ) ); - break; - case bmic: - new_index = std::move( bmic_type( first, last ) ); - break; - } - } - close(); - wipe( p ); - - _index = std::move( new_index ); - _type = type; - - set_revision( rev ); - set_next_id( id ); - } - - id_type next_id() - { - return boost::apply_visitor( - []( auto& index ){ return index.next_id(); }, - _index - ); - } - - void set_next_id( id_type id ) - { - return boost::apply_visitor( - [=]( auto& index ){ return index.set_next_id( id ); }, - _index - ); - } - - int64_t revision() - { - return boost::apply_visitor( - []( auto& index ){ return index.revision(); }, - _index - ); - } - - int64_t set_revision( int64_t rev ) - { - return boost::apply_visitor( - [&rev]( auto& index ){ return index.set_revision( rev ); }, - _index - ); - } - - template< typename ...Args > - std::pair< iter_type, bool > - emplace( Args&&... args ) - { - return boost::apply_visitor( - [&]( auto& index ) - { - auto result = index.emplace( std::forward( args )... ); - return std::pair< iter_type, bool >( iter_type::create( std::move( result.first ) ), result.second ); - }, - _index - ); - } - - template< typename Modifier > - bool modify( iter_type position, Modifier&& mod ) - { - bool result = false; - - switch( _type ) - { - case mira: - result = boost::get< mira_type >( _index ).modify( position.template get< mira_iter_type >(), mod ); - break; - case bmic: - result = boost::get< bmic_type >( _index ).modify( position.template get< bmic_iter_type >(), mod ); - break; - } - - return result; - } - - iter_type erase( iter_type position ) - { - switch( _type ) - { - case mira: - return iter_type::create( boost::get< mira_type >( _index ).erase( position.template get< mira_iter_type >() ) ); - break; - case bmic: - return iter_type::create( boost::get< bmic_type >( _index ).erase( position.template get< bmic_iter_type >() ) ); - break; - } - - assert( false ); - return iter_type(); - } - - iter_type iterator_to( const value_type& v ) - { - return boost::apply_visitor( - [&]( auto& index ){ return iter_type::create( index.iterator_to( v ) ); }, - _index - ); - } - - template< typename CompatibleKey > - iter_type find( const CompatibleKey& k )const - { - return boost::apply_visitor( - [&k]( auto& index ){ return iter_type::create( index.find( k ) ); }, - _index - ); - } - - template< typename CompatibleKey > - iter_type lower_bound( const CompatibleKey& k )const - { - return boost::apply_visitor( - [&k]( auto& index ){ return iter_type::create( index.lower_bound( k ) ); }, - _index - ); - } - - template< typename CompatibleKey > - iter_type upper_bound( const CompatibleKey& k )const - { - return boost::apply_visitor( - [&k]( auto& index ){ return iter_type::create( index.upper_bound( k ) ); }, - _index - ); - } - - template< typename CompatibleKey > - std::pair< iter_type, iter_type > equal_range( const CompatibleKey& k )const - { - return boost::apply_visitor( - [&k]( auto& index ) - { - auto result = index.equal_range( k ); - return std::pair< iter_type, iter_type >( - iter_type::create( std::move( result.first ) ), - iter_type::create( std::move( result.second ) ) - ); - }, - _index - ); - } - - iter_type begin()const - { - return boost::apply_visitor( - []( auto& index ){ return iter_type::create( index.begin() ); }, - _index - ); - } - - iter_type end()const - { - return boost::apply_visitor( - []( auto& index ){ return iter_type::create( index.end() ); }, - _index - ); - } - - rev_iter_type rbegin()const - { - return boost::make_reverse_iterator( end() ); - } - - rev_iter_type rend()const - { - return boost::make_reverse_iterator( begin() ); - } - - bool open( const boost::filesystem::path& p, const boost::any& o ) - { - return boost::apply_visitor( - [&p, &o]( auto& index ){ return index.open( p, o ); }, - _index - ); - } - - void close() - { - boost::apply_visitor( - []( auto& index ){ index.close(); }, - _index - ); - } - - void wipe( const boost::filesystem::path& p ) - { - boost::apply_visitor( - [&]( auto& index ){ index.wipe( p ); }, - _index - ); - } - - void clear() - { - boost::apply_visitor( - []( auto& index ){ index.clear(); }, - _index - ); - } - - void flush() - { - boost::apply_visitor( - []( auto& index ){ index.flush(); }, - _index - ); - } - - size_t size()const - { - return boost::apply_visitor( - []( auto& index ){ return index.size(); }, - _index - ); - } - - allocator_type get_allocator()const - { - return allocator_type(); - } - - template< typename MetaKey, typename MetaValue > - bool put_metadata( const MetaKey& k, const MetaValue& v ) - { - return boost::apply_visitor( - [&k, &v]( auto& index ){ return index.put_metadata( k, v ); }, - _index - ); - } - - template< typename MetaKey, typename MetaValue > - bool get_metadata( const MetaKey& k, MetaValue& v ) - { - return boost::apply_visitor( - [&k, &v]( auto& index ){ return index.get_metadata( k, v ); }, - _index - ); - } - - size_t get_cache_usage()const - { - return boost::apply_visitor( - []( auto& index ){ return index.get_cache_usage(); }, - _index - ); - } - - size_t get_cache_size()const - { - return boost::apply_visitor( - []( auto& index ){ return index.get_cache_size(); }, - _index - ); - } - - void dump_lb_call_counts() - { - boost::apply_visitor( - []( auto& index ){ index.dump_lb_call_counts(); }, - _index - ); - } - - void trim_cache() - { - boost::apply_visitor( - []( auto& index ){ index.trim_cache(); }, - _index - ); - } - - void print_stats()const - { - boost::apply_visitor( - []( auto& index ){ index.print_stats(); }, - _index - ); - } - - private: - index_variant _index; - index_type _type = mira; -}; - -} diff --git a/libraries/mira/include/mira/index_converter.hpp b/libraries/mira/include/mira/index_converter.hpp deleted file mode 100644 index fdfcc76e58..0000000000 --- a/libraries/mira/include/mira/index_converter.hpp +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - - -#define MIRA_ADAPTER_PP_ENUM_PARAMS_MIRA_M(z, n, param) BOOST_PP_COMMA_IF(n) typename index_converter< param ## n >::mira_type -#define MIRA_ADAPTER_PP_ENUM_PARAMS_MIRA(count, param) BOOST_PP_REPEAT(count, MIRA_ADAPTER_PP_ENUM_PARAMS_MIRA_M, param) - -#define MIRA_ADAPTER_PP_ENUM_PARAMS_BOOST_M(z, n, param) BOOST_PP_COMMA_IF(n) typename index_converter< param ## n >::bmic_type -#define MIRA_ADAPTER_PP_ENUM_PARAMS_BOOST(count, param) BOOST_PP_REPEAT(count, MIRA_ADAPTER_PP_ENUM_PARAMS_BOOST_M, param) - -#define MIRA_COMPOSITE_KEY_SIZE 10 - -#define MIRA_CK_ENUM(macro,data) \ - BOOST_PP_ENUM(MIRA_COMPOSITE_KEY_SIZE,macro,data) - -#define MIRA_CK_ENUM_PARAMS(param) \ - BOOST_PP_ENUM_PARAMS(MIRA_COMPOSITE_KEY_SIZE,param) - -#define MIRA_CK_TEMPLATE_PARM(z,n,text) \ - typename BOOST_PP_CAT(text,n) - -#define MIRA_INDEXED_BY_SIZE 20 - -#define MIRA_INDEXED_BY_TEMPLATE_PARM(z,n,var) \ - typename BOOST_PP_CAT(var,n) - -namespace mira { - -template< typename T > -struct index_converter -{ - typedef T mira_type; - typedef T bmic_type; -}; - -template< typename Arg1, typename Arg2, typename Arg3 > -struct index_converter< multi_index::multi_index_container< Arg1, Arg2, Arg3 > > -{ - typedef multi_index::multi_index_container< - typename index_converter< Arg1 >::mira_type, - typename index_converter< Arg2 >::mira_type, - typename index_converter< Arg3 >::mira_type - > mira_type; - - typedef boost_multi_index_adapter< - typename index_converter< Arg1 >::bmic_type, - typename index_converter< Arg2 >::bmic_type, - typename index_converter< Arg3 >::bmic_type - > bmic_type; -}; - -template< - BOOST_PP_ENUM(MIRA_INDEXED_BY_SIZE,MIRA_INDEXED_BY_TEMPLATE_PARM,T) -> -struct index_converter< multi_index::indexed_by< BOOST_PP_ENUM_PARAMS(MIRA_INDEXED_BY_SIZE,T) > > -{ - typedef multi_index::indexed_by< MIRA_ADAPTER_PP_ENUM_PARAMS_MIRA(MIRA_INDEXED_BY_SIZE,T) > mira_type; - typedef boost::multi_index::indexed_by< MIRA_ADAPTER_PP_ENUM_PARAMS_BOOST(MIRA_INDEXED_BY_SIZE,T) > bmic_type; -}; - -template< typename Arg1, typename Arg2, typename Arg3 > -struct index_converter< multi_index::ordered_unique< Arg1, Arg2, Arg3 > > -{ - typedef multi_index::ordered_unique< - typename index_converter< Arg1 >::mira_type, - typename index_converter< Arg2 >::mira_type, - typename index_converter< Arg3 >::mira_type - > mira_type; - - typedef boost::multi_index::ordered_unique< - typename index_converter< Arg1 >::bmic_type, - typename index_converter< Arg2 >::bmic_type, - typename index_converter< Arg3 >::bmic_type - > bmic_type; -}; - -template< typename Value, MIRA_CK_ENUM(MIRA_CK_TEMPLATE_PARM,KeyFromValue) > -struct index_converter< multi_index::composite_key< Value, MIRA_CK_ENUM_PARAMS(KeyFromValue) > > -{ - typedef multi_index::composite_key< Value, MIRA_CK_ENUM_PARAMS(KeyFromValue) > mira_type; - typedef boost::multi_index::composite_key< Value, MIRA_CK_ENUM_PARAMS(KeyFromValue) > bmic_type; -}; - -template< MIRA_CK_ENUM(MIRA_CK_TEMPLATE_PARM,Compare) > -struct index_converter< multi_index::composite_key_compare< MIRA_CK_ENUM_PARAMS(Compare) > > -{ - typedef multi_index::composite_key_compare< MIRA_CK_ENUM_PARAMS(Compare) > mira_type; - typedef boost::multi_index::composite_key_compare< MIRA_CK_ENUM_PARAMS(Compare) > bmic_type; -}; - -} //mira - -#undef MIRA_ADAPTER_PP_ENUM_PARAMS_MIRA_M -#undef MIRA_ADAPTER_PP_ENUM_PARAMS_MIRA -#undef MIRA_ADAPTER_PP_ENUM_PARAMS_BOOST_M -#undef MIRA_ADAPTER_PP_ENUM_PARAMS_BOOST -#undef MIRA_COMPOSITE_KEY_SIZE -#undef MIRA_CK_ENUM -#undef MIRA_CK_ENUM_PARAMS -#undef MIRA_CK_TEMPLATE_PARM -#undef MIRA_INDEXED_BY_SIZE -#undef MIRA_INDEXED_BY_TEMPLATE_PARM diff --git a/libraries/mira/include/mira/indexed_by.hpp b/libraries/mira/include/mira/indexed_by.hpp deleted file mode 100644 index cf7b7baa3f..0000000000 --- a/libraries/mira/include/mira/indexed_by.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -template< typename... Args > -using indexed_by = boost::multi_index::indexed_by< Args... >; - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/iterator_adapter.hpp b/libraries/mira/include/mira/iterator_adapter.hpp deleted file mode 100644 index acec8a9338..0000000000 --- a/libraries/mira/include/mira/iterator_adapter.hpp +++ /dev/null @@ -1,150 +0,0 @@ -#pragma once -#include - -#include - -namespace mira { - -namespace multi_index { -namespace detail { -template< typename Value, typename Key, typename KeyFromValue, - typename KeyCompare, typename ID, typename IDFromValue > - struct rocksdb_iterator; -} } - -template< typename ValueType, typename... Iters > -class iterator_adapter : - public boost::bidirectional_iterator_helper< - iterator_adapter< ValueType, Iters... >, - ValueType, - std::size_t, - const ValueType*, - const ValueType& > -{ - typedef boost::variant< Iters... > iter_variant; - - template< typename t > struct type {}; - - public: - mutable iter_variant _itr; - - iterator_adapter() = default; - iterator_adapter( const iterator_adapter& rhs ) = default; - iterator_adapter( iterator_adapter&& rhs ) = default; - - template< typename T > - static iterator_adapter create( const T& rhs ) - { - iterator_adapter itr; - itr._itr = rhs; - return itr; - } - - template< typename T > - static iterator_adapter create( T&& rhs ) - { - iterator_adapter itr; - itr._itr = std::move( rhs ); - return itr; - } - - bool valid() const - { - return boost::apply_visitor( - [this]( auto& itr ) { return validate_iterator(itr); }, - _itr - ); - } - - template< typename Value, typename Key, typename KeyFromValue, - typename KeyCompare, typename ID, typename IDFromValue > - bool validate_iterator( const multi_index::detail::rocksdb_iterator& i ) const - { - return i.valid(); - } - - template - bool validate_iterator( const T& i ) const - { - return true; - } - - iterator_adapter& operator ++() - { - boost::apply_visitor( - []( auto& itr ){ ++itr; }, - _itr - ); - - return *this; - } - - iterator_adapter operator ++(int)const - { - return boost::apply_visitor( - [this]( auto& itr ) - { - iterator_adapter copy( *this ); - itr++; - return copy; - }, - _itr - ); - } - - iterator_adapter& operator --() - { - boost::apply_visitor( - []( auto& itr ){ --itr; }, - _itr - ); - - return *this; - } - - iterator_adapter operator --(int)const - { - return boost::apply_visitor( - [this]( auto& itr ) - { - iterator_adapter copy( *this ); - itr--; - return copy; - }, - _itr - ); - } - - const ValueType& operator *() const - { - return *(operator ->()); - } - - const ValueType* operator ->() const - { - return boost::apply_visitor( - []( auto& itr ){ return itr.operator->(); }, - _itr - ); - } - - iterator_adapter& operator =( const iterator_adapter& rhs ) = default; - iterator_adapter& operator =( iterator_adapter&& rhs ) = default; - - template< typename IterType > - IterType& get() { return boost::get< IterType >( _itr ); } -}; - -template< typename ValueType, typename... Iters > -bool operator ==( const iterator_adapter< ValueType, Iters... >& lhs, const iterator_adapter< ValueType, Iters... >& rhs ) -{ - return lhs._itr == rhs._itr; -} - -template< typename ValueType, typename... Iters > -bool operator !=( const iterator_adapter< ValueType, Iters... >& lhs, const iterator_adapter< ValueType, Iters... >& rhs ) -{ - return !( lhs == rhs ); -} - -} // mira diff --git a/libraries/mira/include/mira/mem_fun.hpp b/libraries/mira/include/mira/mem_fun.hpp deleted file mode 100644 index 6f78808b11..0000000000 --- a/libraries/mira/include/mira/mem_fun.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright 2003-2015 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -/* mem_fun implements a read-only key extractor based on a given non-const - * member function of a class. - * const_mem_fun does the same for const member functions. - * Additionally, mem_fun and const_mem_fun are overloaded to support - * referece_wrappers of T and "chained pointers" to T's. By chained pointer - * to T we mean a type P such that, given a p of Type P - * *...n...*x is convertible to T&, for some n>=1. - * Examples of chained pointers are raw and smart pointers, iterators and - * arbitrary combinations of these (vg. T** or unique_ptr.) - */ - -template -using const_mem_fun = boost::multi_index::const_mem_fun< Class, Type, PtrToMemberFunction >; - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/member.hpp b/libraries/mira/include/mira/member.hpp deleted file mode 100644 index 865d89f083..0000000000 --- a/libraries/mira/include/mira/member.hpp +++ /dev/null @@ -1,226 +0,0 @@ -#pragma once - -#include /* keep it first to prevent nasty warns in MSVC */ -#include -#include -#include -#include - -#if !defined(BOOST_NO_SFINAE) -#include -#endif - -namespace mira{ - -template class reference_wrapper; /* fwd decl. */ - -namespace multi_index{ - -namespace detail{ - -/* member is a read/write key extractor for accessing a given - * member of a class. - * Additionally, member is overloaded to support referece_wrappers - * of T and "chained pointers" to T's. By chained pointer to T we mean - * a type P such that, given a p of Type P - * *...n...*x is convertible to T&, for some n>=1. - * Examples of chained pointers are raw and smart pointers, iterators and - * arbitrary combinations of these (vg. T** or unique_ptr.) - */ - -template -struct const_member_base -{ - typedef Type result_type; - - template - Type& operator()(const ChainedPtr& x)const - { - return operator()(*x); - } - - Type& operator()(const Class& x)const - { - return x.*PtrToMember; - } - - Type& operator()(const reference_wrapper& x)const - { - return operator()(x.get()); - } - - Type& operator()(const reference_wrapper& x)const - { - return operator()(x.get()); - } -}; - -template -struct non_const_member_base -{ - typedef Type result_type; - - template - -#if !defined(BOOST_NO_SFINAE) - typename boost::disable_if< - boost::is_convertible,Type&>::type -#else - Type& -#endif - - operator()(const ChainedPtr& x)const - { - return operator()(*x); - } - - const Type& operator()(const Class& x)const - { - return x.*PtrToMember; - } - - Type& operator()(Class& x)const - { - return x.*PtrToMember; - } - - const Type& operator()(const reference_wrapper& x)const - { - return operator()(x.get()); - } - - Type& operator()(const reference_wrapper& x)const - { - return operator()(x.get()); - } -}; - -} /* namespace multi_index::detail */ - -template -struct member: - boost::mpl::if_c< - boost::is_const::value, - detail::const_member_base, - detail::non_const_member_base - >::type -{ -}; - -namespace detail{ - -/* MSVC++ 6.0 does not support properly pointers to members as - * non-type template arguments, as reported in - * http://support.microsoft.com/default.aspx?scid=kb;EN-US;249045 - * A similar problem (though not identical) is shown by MSVC++ 7.0. - * We provide an alternative to member<> accepting offsets instead - * of pointers to members. This happens to work even for non-POD - * types (although the standard forbids use of offsetof on these), - * so it serves as a workaround in this compiler for all practical - * purposes. - * Surprisingly enough, other compilers, like Intel C++ 7.0/7.1 and - * Visual Age 6.0, have similar bugs. This replacement of member<> - * can be used for them too. - * - * Support for such old compilers is dropped and - * [non_]const_member_offset_base is deprecated. - */ - -template -struct const_member_offset_base -{ - typedef Type result_type; - - template - -#if !defined(BOOST_NO_SFINAE) - typename boost::disable_if< - boost::is_convertible,Type&>::type -#else - Type& -#endif - - operator()(const ChainedPtr& x)const - { - return operator()(*x); - } - - Type& operator()(const Class& x)const - { - return *static_cast( - static_cast( - static_cast( - static_cast(&x))+OffsetOfMember)); - } - - Type& operator()(const reference_wrapper& x)const - { - return operator()(x.get()); - } - - Type& operator()(const reference_wrapper& x)const - { - return operator()(x.get()); - } -}; - -template -struct non_const_member_offset_base -{ - typedef Type result_type; - - template - -#if !defined(BOOST_NO_SFINAE) - typename boost::disable_if< - boost::is_convertible,Type&>::type -#else - Type& -#endif - - operator()(const ChainedPtr& x)const - { - return operator()(*x); - } - - const Type& operator()(const Class& x)const - { - return *static_cast( - static_cast( - static_cast( - static_cast(&x))+OffsetOfMember)); - } - - Type& operator()(Class& x)const - { - return *static_cast( - static_cast( - static_cast(static_cast(&x))+OffsetOfMember)); - } - - const Type& operator()(const reference_wrapper& x)const - { - return operator()(x.get()); - } - - Type& operator()(const reference_wrapper& x)const - { - return operator()(x.get()); - } -}; - -} /* namespace multi_index::detail */ - -template -struct member_offset: - boost::mpl::if_c< - boost::is_const::value, - detail::const_member_offset_base, - detail::non_const_member_offset_base - >::type -{ -}; - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/mira.hpp b/libraries/mira/include/mira/mira.hpp deleted file mode 100644 index d6ea773819..0000000000 --- a/libraries/mira/include/mira/mira.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -namespace mira { - -template< typename Value, typename IndexSpecifierList, typename Allocator > -struct multi_index_adapter -{ - typedef boost::multi_index_container< Value, IndexSpecifierList, Allocator > boost_container; - typedef multi_index_container< Value, IndexSpecifierList, Allocator > mira_container; -}; - -} \ No newline at end of file diff --git a/libraries/mira/include/mira/multi_index_container.hpp b/libraries/mira/include/mira/multi_index_container.hpp deleted file mode 100644 index d6576fafb7..0000000000 --- a/libraries/mira/include/mira/multi_index_container.hpp +++ /dev/null @@ -1,938 +0,0 @@ -/* Multiply indexed container. - * - * Copyright 2003-2018 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - - -#include /* keep it first to prevent nasty warns in MSVC */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include - -#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) -#include -#endif - -#define BOOST_MULTI_INDEX_CHECK_INVARIANT_OF(x) -#define BOOST_MULTI_INDEX_CHECK_INVARIANT - -#define DEFAULT_COLUMN 0 -#define MIRA_MAX_OPEN_FILES_PER_DB 64 - -#define ENTRY_COUNT_KEY "ENTRY_COUNT" -#define REVISION_KEY "REV" - -namespace mira{ - -namespace multi_index{ - -#if BOOST_WORKAROUND(BOOST_MSVC,BOOST_TESTED_AT(1500)) -#pragma warning(push) -#pragma warning(disable:4522) /* spurious warning on multiple operator=()'s */ -#endif - -template -class multi_index_container: - public detail::multi_index_base_type< - Value,IndexSpecifierList,Allocator>::type -{ - -private: - BOOST_COPYABLE_AND_MOVABLE(multi_index_container) - - template friend class detail::index_base; - - typedef typename detail::multi_index_base_type< - Value,IndexSpecifierList,Allocator>::type super; - - int64_t _revision = -1; - - std::string _name; - std::shared_ptr< ::rocksdb::Statistics > _stats; - ::rocksdb::WriteOptions _wopts; - - rocksdb::ReadOptions _ropts; - -public: - /* All types are inherited from super, a few are explicitly - * brought forward here to save us some typename's. - */ - - typedef typename super::ctor_args_list ctor_args_list; - typedef IndexSpecifierList index_specifier_type_list; - - typedef typename super::index_type_list index_type_list; - - typedef typename super::iterator_type_list iterator_type_list; - typedef typename super::const_iterator_type_list const_iterator_type_list; - typedef typename super::value_type value_type; - typedef typename value_type::id_type id_type; - typedef typename super::final_allocator_type allocator_type; - typedef typename super::iterator iterator; - typedef typename super::const_iterator const_iterator; - - typedef typename super::primary_index_type primary_index_type; - - typedef typename primary_index_type::iterator primary_iterator; - typedef typename primary_index_type::reverse_iterator primary_reverse_iterator; - - static const size_t node_size = sizeof(value_type); - - BOOST_STATIC_ASSERT( - detail::no_duplicate_tags_in_index_list::value); - - /* global project() needs to see this publicly */ - - /* construct/copy/destroy */ - - multi_index_container(): - super(ctor_args_list()), - entry_count(0) - { - std::vector< std::string > split_v; - auto type = boost::core::demangle( typeid( Value ).name() ); - boost::split( split_v, type, boost::is_any_of( ":" ) ); - _wopts.disableWAL = true; - - _name = "rocksdb_" + *(split_v.rbegin()); - } - - explicit multi_index_container( const boost::filesystem::path& p, const boost::any& cfg ): - super(ctor_args_list()), - entry_count(0) - { - std::vector< std::string > split_v; - auto type = boost::core::demangle( typeid( Value ).name() ); - boost::split( split_v, type, boost::is_any_of( ":" ) ); - - _name = "rocksdb_" + *(split_v.rbegin()); - _wopts.disableWAL = true; - - open( p, cfg ); - - BOOST_MULTI_INDEX_CHECK_INVARIANT; - } - - template< typename InputIterator > - explicit multi_index_container( InputIterator& first, InputIterator& last, const boost::filesystem::path& p, const boost::any& cfg ): - super(ctor_args_list()), - entry_count(0) - { - std::vector< std::string > split_v; - auto type = boost::core::demangle( typeid( Value ).name() ); - boost::split( split_v, type, boost::is_any_of( ":" ) ); - - _name = "rocksdb_" + *(split_v.rbegin()); - _wopts.disableWAL = true; - - open( p, cfg ); - - while( first != last ) - { - insert_( *(const_cast(first.operator->())) ); - ++first; - ++entry_count; - } - - BOOST_MULTI_INDEX_CHECK_INVARIANT; - } - - // This really shouldn't be done but is needed for boost variant - multi_index_container( const multi_index_container& other ) : - super( other ), - _revision( other._revision ), - _name( other._name ), - _stats( other._stats ), - _wopts( other._wopts ), - _ropts( other._ropts ), - entry_count( other.entry_count ) - {} - - multi_index_container( multi_index_container&& other ) : - super( std::move( other ) ), - _revision( other._revision ), - _name( std::move( other._name ) ), - _stats( std::move( other._stats ) ), - _wopts( std::move( other._wopts ) ), - _ropts( std::move( other._ropts ) ), - entry_count( other.entry_count ) - {} - - multi_index_container& operator=( const multi_index_container& rhs ) - { - super::operator=( rhs ); - _revision = rhs._revision; - _name = rhs._name; - _stats = rhs._stats; - _wopts = rhs._wopts; - _ropts = rhs._ropts; - entry_count = rhs.entry_count; - - return *this; - } - - multi_index_container& operator=( multi_index_container&& rhs ) - { - super::operator=( std::move( rhs ) ); - _revision = rhs._revision; - _name = std::move( rhs._name ); - _stats = std::move( rhs._stats ); - _wopts = std::move( rhs._wopts ); - _ropts = std::move( rhs._ropts ); - entry_count = rhs.entry_count; - - return *this; - } - - bool open( const boost::filesystem::path& p, const boost::any& cfg = nullptr ) - { - assert( p.is_absolute() ); - - std::string str_path = ( p / _name ).string(); - - maybe_create_schema( str_path ); - - // TODO: Move out of constructor becasuse throwing exceptions in a constuctor is sad... - column_definitions column_defs; - populate_column_definitions_( column_defs ); - - ::rocksdb::Options opts; - - try - { - detail::cache_manager::get()->set_object_threshold( configuration::get_object_count( cfg ) ); - - opts = configuration::get_options( cfg, boost::core::demangle( typeid( Value ).name() ) ); - - if ( configuration::gather_statistics( cfg ) ) - opts.statistics = _stats = ::rocksdb::CreateDBStatistics(); - } - catch ( ... ) - { - elog( "Failure while applying configuration for database: ${db}", - ("db", boost::core::demangle( typeid( Value ).name())) ); - throw; - } - - std::vector< ::rocksdb::ColumnFamilyHandle* > handles; - - ::rocksdb::DB* db = nullptr; - ::rocksdb::Status s = ::rocksdb::DB::Open( opts, str_path, column_defs, &handles, &db ); - - for( ::rocksdb::ColumnFamilyHandle* h : handles ) - { - super::_handles.push_back( std::shared_ptr< ::rocksdb::ColumnFamilyHandle >( h ) ); - } - - if( s.ok() ) - { - // Verify DB Schema - - super::_db.reset( db ); - - ::rocksdb::ReadOptions read_opts; - ::rocksdb::PinnableSlice value_slice; - - auto ser_count_key = fc::raw::pack_to_vector( ENTRY_COUNT_KEY ); - - s = super::_db->Get( - read_opts, - &*super::_handles[ DEFAULT_COLUMN ], - ::rocksdb::Slice( ser_count_key.data(), ser_count_key.size() ), - &value_slice ); - - if( s.ok() ) - { - entry_count = fc::raw::unpack_from_char_array< uint64_t >( value_slice.data(), value_slice.size() ); - } - - auto ser_rev_key = fc::raw::pack_to_vector( REVISION_KEY ); - value_slice.Reset(); - - s = super::_db->Get( - read_opts, - &*super::_handles[ DEFAULT_COLUMN ], - ::rocksdb::Slice( ser_rev_key.data(), ser_rev_key.size() ), - &value_slice ); - - if( s.ok() ) - { - _revision = fc::raw::unpack_from_char_array< int64_t >( value_slice.data(), value_slice.size() ); - } - } - else - { - std::cout << std::string( s.getState() ) << std::endl; - return false; - } - - super::cache_first_key(); - - super::object_cache_factory_type::reset(); - return true; - } - - void close() - { - if( super::_db && super::_db.unique() ) - { - auto ser_count_key = fc::raw::pack_to_vector( ENTRY_COUNT_KEY ); - auto ser_count_val = fc::raw::pack_to_vector( entry_count ); - - super::_db->Put( - _wopts, - &*(super::_handles[ DEFAULT_COLUMN ]), - ::rocksdb::Slice( ser_count_key.data(), ser_count_key.size() ), - ::rocksdb::Slice( ser_count_val.data(), ser_count_val.size() ) ); - - super::_cache->clear(); - rocksdb::CancelAllBackgroundWork( &(*super::_db), true ); - super::cleanup_column_handles(); - super::_db->Close(); - super::_db.reset(); - } - } - - void wipe( const boost::filesystem::path& p ) - { - assert( !(super::_db) ); - - column_definitions column_defs; - populate_column_definitions_( column_defs ); - - auto s = rocksdb::DestroyDB( ( p / _name ).string(), rocksdb::Options(), column_defs ); - - if( !s.ok() ) std::cout << std::string( s.getState() ) << std::endl; - - super::_cache->clear(); - } - - ~multi_index_container() - { - close(); - } - - void flush() - { - if( super::_db ) - { - super::flush(); - } - } - - void trim_cache() - { - detail::cache_manager::get()->adjust_capacity(); - } - - allocator_type get_allocator()const BOOST_NOEXCEPT - { - return allocator_type(); - } - - /* retrieval of indices by number */ - - template - struct nth_index - { - BOOST_STATIC_ASSERT(N>=0&&N::type::value); - typedef typename boost::mpl::at_c::type type; - }; - - template - typename nth_index::type& get()BOOST_NOEXCEPT - { - BOOST_STATIC_ASSERT(N>=0&&N::type::value); - return *this; - } - - template - const typename nth_index::type& get()const BOOST_NOEXCEPT - { - BOOST_STATIC_ASSERT(N>=0&&N::type::value); - return *this; - } - - /* retrieval of indices by tag */ - - template - struct index - { - typedef typename boost::mpl::find_if< - index_type_list, - detail::has_tag - >::type iter; - - BOOST_STATIC_CONSTANT( - bool,index_found=!(boost::is_same::type >::value)); - BOOST_STATIC_ASSERT(index_found); - - typedef typename boost::mpl::deref::type type; - }; - - template - typename index::type& get()BOOST_NOEXCEPT - { - return *this; - } - - template - const typename index::type& get()const BOOST_NOEXCEPT - { - return *this; - } - - /* projection of iterators by number */ -#if !defined(BOOST_NO_MEMBER_TEMPLATES) - template - struct nth_index_iterator - { - typedef typename nth_index::type::iterator type; - }; - - template - struct nth_index_const_iterator - { - typedef typename nth_index::type::const_iterator type; - }; -#endif - - /* projection of iterators by tag */ - -#if !defined(BOOST_NO_MEMBER_TEMPLATES) - template - struct index_iterator - { - typedef typename index::type::iterator type; - }; - - template - struct index_const_iterator - { - typedef typename index::type::const_iterator type; - }; -#endif - -int64_t revision() { return _revision; } - -int64_t set_revision( int64_t rev ) -{ - const static auto ser_rev_key = fc::raw::pack_to_vector( REVISION_KEY ); - const static ::rocksdb::Slice rev_slice( ser_rev_key.data(), ser_rev_key.size() ); - auto ser_rev_val = fc::raw::pack_to_vector( rev ); - - auto s = super::_db->Put( - _wopts, rev_slice, - ::rocksdb::Slice( ser_rev_val.data(), ser_rev_val.size() ) ); - - if( s.ok() ) _revision = rev; - - return _revision; -} - -id_type next_id() -{ - id_type id( 0 ); - if ( !get_metadata( "next_id", id ) ) - id = id_type( 0 ); - return id; -} - -void set_next_id( id_type id ) -{ - bool success = put_metadata( "next_id", id ); - boost::ignore_unused( success ); - assert( success ); -} - -void print_stats() const -{ - if( _stats ) - { - std::cout << _name << " stats:\n"; - std::cout << _stats->ToString() << "\n"; - } -} - -size_t get_cache_usage() const -{ - return super::_cache->usage(); -} - -size_t get_cache_size() const -{ - return super::_cache->size(); -} - -void dump_lb_call_counts() -{ - ilog( "Object ${s}:", ("s",_name) ); - super::dump_lb_call_counts(); - ilog( "" ); -} - -primary_iterator iterator_to( const value_type& x ) -{ - return primary_index_type::iterator_to( x ); -} - -template< typename CompatibleKey > -primary_iterator find( const CompatibleKey& x )const -{ - return primary_index_type::find( x ); -} - -template< typename CompatibleKey > -primary_iterator lower_bound( const CompatibleKey& x )const -{ - return primary_index_type::lower_bound( x ); -} - -primary_iterator upper_bound( const typename primary_index_type::key_type& x )const -{ - return primary_index_type::upper_bound( x ); -} - -template< typename CompatibleKey > -primary_iterator upper_bound( const CompatibleKey& x )const -{ - return primary_index_type::upper_bound( x ); -} - -template< typename LowerBounder > -std::pair< primary_iterator, primary_iterator > -range( LowerBounder& lower, const typename primary_index_type::key_type upper )const -{ - return primary_index_type::range( lower, upper ); -} - -typename primary_index_type::iterator begin() BOOST_NOEXCEPT - { return primary_index_type::begin(); } - -typename primary_index_type::const_iterator begin()const BOOST_NOEXCEPT - { return primary_index_type::begin(); } - -typename primary_index_type::iterator end() BOOST_NOEXCEPT - { return primary_index_type::end(); } - -typename primary_index_type::const_iterator end()const BOOST_NOEXCEPT - { return primary_index_type::end(); } - -typename primary_index_type::reverse_iterator rbegin() BOOST_NOEXCEPT - { return primary_index_type::rbegin(); } - -typename primary_index_type::const_reverse_iterator rbegin() const BOOST_NOEXCEPT - { return primary_index_type::rbegin(); } - -typename primary_index_type::reverse_iterator rend() BOOST_NOEXCEPT - { return primary_index_type::rend(); } - -typename primary_index_type::const_reverse_iterator rend() const BOOST_NOEXCEPT - { return primary_index_type::rend(); } - -typename primary_index_type::const_iterator cbegin()const BOOST_NOEXCEPT - { return primary_index_type::cbegin(); } - -typename primary_index_type::const_iterator cend()const BOOST_NOEXCEPT - { return primary_index_type::cend(); } - -typename primary_index_type::const_reverse_iterator crbegin()const BOOST_NOEXCEPT - { return primary_index_type::crbegin(); } - -typename primary_index_type::const_reverse_iterator crend()const BOOST_NOEXCEPT - { return primary_index_type::crend(); } - -template< typename Modifier > -bool modify( primary_iterator position, Modifier mod ) -{ - return primary_index_type::modify( position, mod ); -} - -primary_iterator erase( primary_iterator position ) -{ - return primary_index_type::erase( position ); -} - - bool empty_()const - { - return entry_count==0; - } - - uint64_t size_()const - { - return entry_count; - } - - uint64_t max_size_()const - { - return static_cast(-1); - } - - template< typename... Args > - bool emplace_rocksdb_( Args&&... args ) - { - Value v( std::forward< Args >(args)... ); - return insert_( v ); - } - - bool insert_( value_type& v ) - { - bool status = false; - if( super::insert_rocksdb_( v ) ) - { - auto retval = super::_db->Write( _wopts, super::_write_buffer.GetWriteBatch() ); - status = retval.ok(); - if( status ) - { - ++entry_count; - super::commit_first_key_update(); - } - else - { - elog( "${e}", ("e", retval.ToString()) ); - super::reset_first_key_update(); - } - } - else - { - super::reset_first_key_update(); - } - super::_write_buffer.Clear(); - - return status; - } - - void erase_( value_type& v ) - { - super::erase_( v ); - auto retval = super::_db->Write( _wopts, super::_write_buffer.GetWriteBatch() ); - bool status = retval.ok(); - if( status ) - { - --entry_count; - std::lock_guard< std::mutex > lock( super::_cache->get_lock() ); - super::_cache->invalidate( v ); - super::commit_first_key_update(); - } - else - { - elog( "${e}", ("e", retval.ToString()) ); - super::reset_first_key_update(); - } - - super::_write_buffer.Clear(); - } - - void clear_() - { - super::clear_(); - super::_cache->clear(); - entry_count=0; - } - - template< typename Modifier > - bool modify_( Modifier& mod, value_type& v ) - { - bool status = false; - std::vector< size_t > modified_indices; - if( super::modify_( mod, v, modified_indices ) ) - { - auto retval = super::_db->Write( _wopts, super::_write_buffer.GetWriteBatch() ); - status = retval.ok(); - - if( status ) - { - /* This gets a little weird because the reference passed in - most likely belongs to the shared ptr in the cache, so updating - the value has already updated the cache, but in case something - doesn't line up here, we update by moving the value to itself... */ - std::lock_guard< std::mutex > lock( super::_cache->get_lock() ); - super::_cache->get_index_cache( ID_INDEX )->update( (void*)&v, std::move( v ), modified_indices ); - super::commit_first_key_update(); - } - else - { - elog( "${e}", ("e", retval.ToString()) ); - super::reset_first_key_update(); - } - } - else - { - super::reset_first_key_update(); - } - - super::_write_buffer.Clear(); - - return status; - } - - template< typename MetaKey, typename MetaValue > - bool get_metadata( const MetaKey& k, MetaValue& v ) - { - if( !super::_db ) return false; - - rocksdb::PinnableSlice key_slice, value_slice; - - pack_to_slice( key_slice, k ); - - auto status = super::_db->Get( - _ropts, - &*super::_handles[ 0 ], - key_slice, - &value_slice ); - - if( status.ok() ) - { - unpack_from_slice( value_slice, v ); - //ilog( "Retrieved metdata for ${type}: ${key},${value}", ("type",boost::core::demangle(typeid(Value).name()))("key",k)("value",v) ); - } - else - { - //ilog( "Failed to retrieve metadata for ${type}: ${key}", ("type",boost::core::demangle(typeid(Value).name()))("key",k) ); - } - - return status.ok(); - } - - template< typename MetaKey, typename MetaValue > - bool put_metadata( const MetaKey& k, const MetaValue& v ) - { - if( !super::_db ) return false; - - rocksdb::PinnableSlice key_slice, value_slice; - pack_to_slice( key_slice, k ); - pack_to_slice( value_slice, v ); - - auto status = super::_db->Put( - _wopts, - &*super::_handles[0], - key_slice, - value_slice ); - - if( status.ok() ) - { - //ilog( "Stored metdata for ${type}: ${key},${value}", ("type",boost::core::demangle(typeid(Value).name()))("key",k)("value",v) ); - } - else - { - //ilog( "Failed to store metadata for ${type}: ${key},${value}", ("type",boost::core::demangle(typeid(Value).name()))("key",k)("value",v) ); - } - - return status.ok(); - } - -private: - uint64_t entry_count; - - size_t get_column_size() const { return super::COLUMN_INDEX; } - - void populate_column_definitions_( column_definitions& defs ) const - { - super::populate_column_definitions_( defs ); - } - - bool maybe_create_schema( const std::string& str_path ) - { - ::rocksdb::DB* db = nullptr; - - column_definitions column_defs; - populate_column_definitions_( column_defs ); - - std::vector< ::rocksdb::ColumnFamilyHandle* > handles; - - ::rocksdb::Options opts; - //opts.IncreaseParallelism(); - //opts.OptimizeLevelStyleCompaction(); - opts.max_open_files = MIRA_MAX_OPEN_FILES_PER_DB; - - ::rocksdb::Status s = ::rocksdb::DB::OpenForReadOnly( opts, str_path, column_defs, &handles, &db ); - - if( s.ok() ) - { - for( auto* h : handles ) delete h; - delete db; - return true; - } - - opts.create_if_missing = true; - - s = ::rocksdb::DB::Open( opts, str_path, &db ); - - if( s.ok() ) - { - column_defs.clear(); - populate_column_definitions_( column_defs ); - column_defs.erase( column_defs.begin() ); - - s = db->CreateColumnFamilies( column_defs, &handles ); - - if( s.ok() ) - { - // Create default column keys - - auto ser_count_key = fc::raw::pack_to_vector( ENTRY_COUNT_KEY ); - auto ser_count_val = fc::raw::pack_to_vector( uint64_t(0) ); - - s = db->Put( - _wopts, - db->DefaultColumnFamily(), - ::rocksdb::Slice( ser_count_key.data(), ser_count_key.size() ), - ::rocksdb::Slice( ser_count_val.data(), ser_count_val.size() ) ); - - if( !s.ok() ) return false; - - auto ser_rev_key = fc::raw::pack_to_vector( REVISION_KEY ); - auto ser_rev_val = fc::raw::pack_to_vector( int64_t(0) ); - - db->Put( - _wopts, - db->DefaultColumnFamily(), - ::rocksdb::Slice( ser_rev_key.data(), ser_rev_key.size() ), - ::rocksdb::Slice( ser_rev_val.data(), ser_rev_val.size() ) ); - - if( !s.ok() ) return false; - - // Save schema info - - for( auto* h : handles ) delete h; - } - else - { - std::cout << std::string( s.getState() ) << std::endl; - } - - delete db; - - return true; - } - else - { - std::cout << std::string( s.getState() ) << std::endl; - } - - return false; - } - -#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING)&&\ - BOOST_WORKAROUND(__MWERKS__,<=0x3003) -#pragma parse_mfunc_templ reset -#endif -}; - -#if BOOST_WORKAROUND(BOOST_MSVC,BOOST_TESTED_AT(1500)) -#pragma warning(pop) /* C4522 */ -#endif - -/* retrieval of indices by number */ - -template -struct nth_index -{ - BOOST_STATIC_CONSTANT( - int, - M=boost::mpl::size::type::value); - BOOST_STATIC_ASSERT(N>=0&&N::type type; -}; - -/* retrieval of indices by tag */ - -template -struct index -{ - typedef typename MultiIndexContainer::index_type_list index_type_list; - - typedef typename boost::mpl::find_if< - index_type_list, - detail::has_tag - >::type iter; - - BOOST_STATIC_CONSTANT( - bool,index_found=!(boost::is_same::type >::value)); - BOOST_STATIC_ASSERT(index_found); - - typedef typename boost::mpl::deref::type type; -}; - -/* projection of iterators by number */ - -template -struct nth_index_iterator -{ - typedef typename nth_index::type::iterator type; -}; - -template -struct nth_index_const_iterator -{ - typedef typename nth_index::type::const_iterator type; -}; - -/* projection of iterators by tag */ - -template -struct index_iterator -{ - typedef typename ::mira::multi_index::index< - MultiIndexContainer,Tag>::type::iterator type; -}; - -template -struct index_const_iterator -{ - typedef typename ::mira::multi_index::index< - MultiIndexContainer,Tag>::type::const_iterator type; -}; - -} /* namespace multi_index */ - -/* Associated global functions are promoted to namespace boost, except - * comparison operators and swap, which are meant to be Koenig looked-up. - */ - -} /* namespace mira */ - -#undef BOOST_MULTI_INDEX_CHECK_INVARIANT -#undef BOOST_MULTI_INDEX_CHECK_INVARIANT_OF diff --git a/libraries/mira/include/mira/multi_index_container_fwd.hpp b/libraries/mira/include/mira/multi_index_container_fwd.hpp deleted file mode 100644 index 9827b808d9..0000000000 --- a/libraries/mira/include/mira/multi_index_container_fwd.hpp +++ /dev/null @@ -1,125 +0,0 @@ -/* Copyright 2003-2013 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include /* keep it first to prevent nasty warns in MSVC */ -#include -#include -#include -#include -#include - -#include - -#include - -#define ID_INDEX 1 - -namespace mira{ - -namespace multi_index{ - -/* Default value for IndexSpecifierList specifies a container - * equivalent to std::set. - */ - -template< - typename Value, - typename IndexSpecifierList=indexed_by > >, - typename Allocator=std::allocator > -class multi_index_container; - -template -struct nth_index; - -template -struct index; - -template -struct nth_index_iterator; - -template -struct nth_index_const_iterator; - -template -struct index_iterator; - -template -struct index_const_iterator; - -/* get and project functions not fwd declared due to problems - * with dependent typenames - */ - -template< - typename Value1,typename IndexSpecifierList1,typename Allocator1, - typename Value2,typename IndexSpecifierList2,typename Allocator2 -> -bool operator==( - const multi_index_container& x, - const multi_index_container& y); - -template< - typename Value1,typename IndexSpecifierList1,typename Allocator1, - typename Value2,typename IndexSpecifierList2,typename Allocator2 -> -bool operator<( - const multi_index_container& x, - const multi_index_container& y); - -template< - typename Value1,typename IndexSpecifierList1,typename Allocator1, - typename Value2,typename IndexSpecifierList2,typename Allocator2 -> -bool operator!=( - const multi_index_container& x, - const multi_index_container& y); - -template< - typename Value1,typename IndexSpecifierList1,typename Allocator1, - typename Value2,typename IndexSpecifierList2,typename Allocator2 -> -bool operator>( - const multi_index_container& x, - const multi_index_container& y); - -template< - typename Value1,typename IndexSpecifierList1,typename Allocator1, - typename Value2,typename IndexSpecifierList2,typename Allocator2 -> -bool operator>=( - const multi_index_container& x, - const multi_index_container& y); - -template< - typename Value1,typename IndexSpecifierList1,typename Allocator1, - typename Value2,typename IndexSpecifierList2,typename Allocator2 -> -bool operator<=( - const multi_index_container& x, - const multi_index_container& y); - -template -void swap( - multi_index_container& x, - multi_index_container& y); - -typedef std::shared_ptr< ::rocksdb::DB > db_ptr; -typedef std::vector< ::rocksdb::ColumnFamilyDescriptor > column_definitions; -typedef std::vector< std::shared_ptr< ::rocksdb::ColumnFamilyHandle > > column_handles; - -} /* namespace multi_index */ - -/* multi_index_container, being the main type of this library, is promoted to - * namespace mira. - */ - -using multi_index::multi_index_container; - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/ordered_index.hpp b/libraries/mira/include/mira/ordered_index.hpp deleted file mode 100644 index e7981192ac..0000000000 --- a/libraries/mira/include/mira/ordered_index.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright 2003-2015 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -#include -#include -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -/* no augment policy for plain ordered indices */ - -#if BOOST_VERSION >= 105900 - -using boost::multi_index::detail::null_augment_policy; - -#else - -struct null_augment_policy -{ - template - struct augmented_interface - { - typedef OrderedIndexImpl type; - }; - - template - struct augmented_node - { - typedef OrderedIndexNodeImpl type; - }; - - template static void add(Pointer,Pointer){} - template static void remove(Pointer,Pointer){} - template static void copy(Pointer,Pointer){} - template static void rotate_left(Pointer,Pointer){} - template static void rotate_right(Pointer,Pointer){} - - template static bool invariant(Pointer){return true;} -}; - -#endif - -} /* namespace multi_index::detail */ - -/* ordered_index specifiers */ - -template -struct ordered_unique -{ - typedef typename detail::ordered_index_args< - Arg1,Arg2,Arg3> index_args; - typedef typename index_args::tag_list_type::type tag_list_type; - typedef typename index_args::key_from_value_type key_from_value_type; - typedef typename index_args::compare_type compare_type; - - template - struct index_class - { - typedef detail::ordered_index< - key_from_value_type,compare_type, - SuperMeta,tag_list_type,detail::ordered_unique_tag, - detail::null_augment_policy> type; - }; -}; - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/ordered_index_fwd.hpp b/libraries/mira/include/mira/ordered_index_fwd.hpp deleted file mode 100644 index c7b2f3fbb7..0000000000 --- a/libraries/mira/include/mira/ordered_index_fwd.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright 2003-2015 Joaquin M Lopez Munoz. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * - * See http://www.boost.org/libs/multi_index for library home page. - */ - -#pragma once - -//#include -//#include - -#include - -namespace mira{ - -namespace multi_index{ - -template -struct ordered_unique; - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/slice_pack.hpp b/libraries/mira/include/mira/slice_pack.hpp deleted file mode 100644 index a939d345c7..0000000000 --- a/libraries/mira/include/mira/slice_pack.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include -#include -#include - -namespace mira { - -using ::rocksdb::Slice; -using ::rocksdb::PinnableSlice; - -template< typename T > struct is_static_length : public boost::false_type {}; -template<> struct is_static_length< bool > : public boost::true_type {}; -template<> struct is_static_length< char > : public boost::true_type {}; -template<> struct is_static_length< int16_t > : public boost::true_type {}; -template<> struct is_static_length< uint16_t > : public boost::true_type {}; -template<> struct is_static_length< int32_t > : public boost::true_type {}; -template<> struct is_static_length< uint32_t > : public boost::true_type {}; -template<> struct is_static_length< int64_t > : public boost::true_type {}; -template<> struct is_static_length< uint64_t > : public boost::true_type {}; - -template<> struct is_static_length< fc::time_point_sec > : public boost::true_type {}; -template<> struct is_static_length< fc::uint128_t > : public boost::true_type {}; -template<> struct is_static_length< fc::sha256 > : public boost::true_type {}; -template< typename T > struct is_static_length< fc::safe< T > > : public is_static_length< T > {}; - -template<> struct is_static_length< boost::tuples::null_type > : public boost::true_type {}; - -template< typename HT, typename TT > -struct is_static_length< boost::tuples::cons< HT, TT > > -{ - static const bool value = is_static_length< HT >::value && is_static_length< TT >::value; -}; - -template< typename T > struct slice_packer -{ - static void pack( PinnableSlice& s, const T& t ) - { - if( is_static_length< T >::value ) - { - s.PinSelf( Slice( (char*)&t, sizeof(t) ) ); - } - else - { - auto v = fc::raw::pack_to_vector( t ); - s.PinSelf( Slice( v.data(), v.size() ) ); - } - } - - static void unpack( const Slice& s, T& t ) - { - if( is_static_length< T >::value ) - { - t = *(T*)s.data(); - } - else - { - fc::raw::unpack_from_char_array< T >( s.data(), s.size(), t ); - } - } -}; - -template< typename T > -void pack_to_slice( PinnableSlice& s, const T& t ) -{ - slice_packer< T >::pack( s, t ); -} - -template< typename T > -void unpack_from_slice( const Slice& s, T& t ) -{ - slice_packer< T >::unpack( s, t ); -} - -template< typename T > -T unpack_from_slice( const Slice& s ) -{ - T t; - unpack_from_slice( s, t ); - return t; -} - -} // mira diff --git a/libraries/mira/include/mira/tag.hpp b/libraries/mira/include/mira/tag.hpp deleted file mode 100644 index c3c79f143e..0000000000 --- a/libraries/mira/include/mira/tag.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -namespace mira{ - -namespace multi_index{ - -namespace detail{ - -using boost::multi_index::detail::tag_marker; - -template -using is_tag = boost::multi_index::detail::is_tag< T >; - -} /* namespace multi_index::detail */ - -template< typename T > -using tag = boost::multi_index::tag< T >; - -} /* namespace multi_index */ - -} /* namespace mira */ diff --git a/libraries/mira/include/mira/well_ordered.hpp b/libraries/mira/include/mira/well_ordered.hpp deleted file mode 100644 index 2fd73f27d2..0000000000 --- a/libraries/mira/include/mira/well_ordered.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once -#include - -#include - -#include -#include -#include - -#define MIRA_WELL_ORDERED_TYPE( t ) \ -template< bool ROOT > struct is_well_ordered< t, ROOT > : public boost::true_type {}; - -namespace mira { - - using multi_index::composite_key_compare; - using multi_index::detail::slice_comparator; - - /* - Partial composite keys will lookup in rocksdb by filling in the missing parts with - default initialized values. We are defining a type well-ordered if the default value - is the lowest value. - */ - template< typename T, bool ROOT > struct is_well_ordered - { - static const bool value = ROOT; - }; - - template< typename T, bool ROOT > struct is_well_ordered< std::less< T >, ROOT > : is_well_ordered< T, ROOT > {}; - template< bool ROOT > struct is_well_ordered< boost::tuples::null_type, ROOT > : boost::true_type {}; - - template< typename HT, typename TT, bool ROOT > - struct is_well_ordered< boost::tuples::cons< HT, TT >, ROOT > - { - static const bool value = is_well_ordered< HT, ROOT >::value() && is_well_ordered< TT, false >::value; - }; - - template< typename... Args, bool ROOT > - struct is_well_ordered< composite_key_compare< Args... >, ROOT > - { - static const bool value = is_well_ordered< typename composite_key_compare< Args... >::key_comp_tuple, ROOT >::value; - }; - - template< typename Key, typename CompareType, bool ROOT > - struct is_well_ordered< slice_comparator< Key, CompareType >, ROOT > - { - static const bool value = is_well_ordered< CompareType, ROOT >::value; - }; - - MIRA_WELL_ORDERED_TYPE( char ) - MIRA_WELL_ORDERED_TYPE( uint16_t ) - MIRA_WELL_ORDERED_TYPE( uint32_t ) - MIRA_WELL_ORDERED_TYPE( uint64_t ) - - MIRA_WELL_ORDERED_TYPE( fc::time_point_sec ) - MIRA_WELL_ORDERED_TYPE( fc::uint128_t ) - MIRA_WELL_ORDERED_TYPE( fc::sha256 ) - - template< typename T, bool ROOT > struct is_well_ordered< fc::safe< T >, ROOT > : public is_well_ordered< T, ROOT > {}; -} diff --git a/libraries/mira/src/configuration.cpp b/libraries/mira/src/configuration.cpp deleted file mode 100644 index a2d4b30cf3..0000000000 --- a/libraries/mira/src/configuration.cpp +++ /dev/null @@ -1,318 +0,0 @@ -#include - -namespace mira { - -// Base configuration for an index -#define BASE "base" - -// Global options -#define GLOBAL "global" -#define SHARED_CACHE "shared_cache" -#define WRITE_BUFFER_MANAGER "write_buffer_manager" -#define OBJECT_COUNT "object_count" -#define STATISTICS "statistics" - -// Write buffer manager options -#define WRITE_BUFFER_SIZE "write_buffer_size" - -// Shared cache options -#define CAPACITY "capacity" -#define NUM_SHARD_BITS "num_shard_bits" - -// Database options -#define ALLOW_MMAP_READS "allow_mmap_reads" -#define WRITE_BUFFER_SIZE "write_buffer_size" -#define MAX_BYTES_FOR_LEVEL_BASE "max_bytes_for_level_base" -#define TARGET_FILE_SIZE_BASE "target_file_size_base" -#define MAX_WRITE_BUFFER_NUMBER "max_write_buffer_number" -#define MAX_BACKGROUND_COMPACTIONS "max_background_compactions" -#define MAX_BACKGROUND_FLUSHES "max_background_flushes" -#define MIN_WRITE_BUFFER_NUMBER_TO_MERGE "min_write_buffer_number_to_merge" -#define OPTIMIZE_LEVEL_STYLE_COMPACTION "optimize_level_style_compaction" -#define INCREASE_PARALLELISM "increase_parallelism" -#define BLOCK_BASED_TABLE_OPTIONS "block_based_table_options" -#define BLOCK_SIZE "block_size" -#define BLOOM_FILTER_POLICY "bloom_filter_policy" -#define BITS_PER_KEY "bits_per_key" -#define USE_BLOCK_BASED_BUILDER "use_block_based_builder" -#define CACHE_INDEX_AND_FILTER_BLOCKS "cache_index_and_filter_blocks" - -static std::shared_ptr< rocksdb::Cache > global_shared_cache; -static std::shared_ptr< rocksdb::WriteBufferManager > global_write_buffer_manager; - -static std::map< std::string, std::function< void( ::rocksdb::Options&, fc::variant ) > > global_database_option_map { - { ALLOW_MMAP_READS, []( ::rocksdb::Options& o, fc::variant v ) { o.allow_mmap_reads = v.as< bool >(); } }, - { WRITE_BUFFER_SIZE, []( ::rocksdb::Options& o, fc::variant v ) { o.write_buffer_size = v.as< uint64_t >(); } }, - { MAX_BYTES_FOR_LEVEL_BASE, []( ::rocksdb::Options& o, fc::variant v ) { o.max_bytes_for_level_base = v.as< uint64_t >(); } }, - { TARGET_FILE_SIZE_BASE, []( ::rocksdb::Options& o, fc::variant v ) { o.target_file_size_base = v.as< uint64_t >(); } }, - { MAX_WRITE_BUFFER_NUMBER, []( ::rocksdb::Options& o, fc::variant v ) { o.max_write_buffer_number = v.as< int >(); } }, - { MAX_BACKGROUND_COMPACTIONS, []( ::rocksdb::Options& o, fc::variant v ) { o.max_background_compactions = v.as< int >(); } }, - { MAX_BACKGROUND_FLUSHES, []( ::rocksdb::Options& o, fc::variant v ) { o.max_background_flushes = v.as< int >(); } }, - { MIN_WRITE_BUFFER_NUMBER_TO_MERGE, []( ::rocksdb::Options& o, fc::variant v ) { o.min_write_buffer_number_to_merge = v.as< int >(); } }, - { OPTIMIZE_LEVEL_STYLE_COMPACTION, []( ::rocksdb::Options& o, fc::variant v ) - { - if ( v.as< bool >() ) - o.OptimizeLevelStyleCompaction(); - } - }, - { INCREASE_PARALLELISM, []( ::rocksdb::Options& o, fc::variant v ) - { - if ( v.as< bool >() ) - o.IncreaseParallelism(); - } - }, - { BLOCK_BASED_TABLE_OPTIONS, []( ::rocksdb::Options& o, auto v ) - { - ::rocksdb::BlockBasedTableOptions table_options; - FC_ASSERT( v.is_object(), "Expected '${key}' to be an object", - ("key", BLOCK_BASED_TABLE_OPTIONS) ); - - auto& obj = v.get_object(); - - table_options.block_cache = global_shared_cache; - - if ( obj.contains( BLOCK_SIZE ) ) - { - FC_ASSERT( obj[ BLOCK_SIZE ].is_uint64(), "Expected '${key}' to be an unsigned integer", - ("key", BLOCK_SIZE) ); - - table_options.block_size = obj[ BLOCK_SIZE ].template as< uint64_t >(); - } - - if ( obj.contains( CACHE_INDEX_AND_FILTER_BLOCKS ) ) - { - FC_ASSERT( obj[ CACHE_INDEX_AND_FILTER_BLOCKS ].is_bool(), "Expected '${key}' to be a boolean", - ("key", CACHE_INDEX_AND_FILTER_BLOCKS) ); - - table_options.cache_index_and_filter_blocks = obj[ CACHE_INDEX_AND_FILTER_BLOCKS ].template as< bool >(); - } - - if ( obj.contains( BLOOM_FILTER_POLICY ) ) - { - FC_ASSERT( obj[ BLOOM_FILTER_POLICY ].is_object(), "Expected '${key}' to be an object", - ("key", BLOOM_FILTER_POLICY) ); - - auto filter_policy = obj[ BLOOM_FILTER_POLICY ].get_object(); - size_t bits_per_key; - - // Bits per key is required for the bloom filter policy - FC_ASSERT( filter_policy.contains( BITS_PER_KEY ), "Expected '${parent}' to contain '${key}'", - ("parent", BLOOM_FILTER_POLICY) - ("key", BITS_PER_KEY) ); - - FC_ASSERT( filter_policy[ BITS_PER_KEY ].is_uint64(), "Expected '${key}' to be an unsigned integer", - ("key", BITS_PER_KEY) ); - - bits_per_key = filter_policy[ BITS_PER_KEY ].template as< uint64_t >(); - - if ( filter_policy.contains( USE_BLOCK_BASED_BUILDER ) ) - { - FC_ASSERT( filter_policy[ USE_BLOCK_BASED_BUILDER ].is_bool(), "Expected '${key}' to be a boolean", - ("key", USE_BLOCK_BASED_BUILDER) ); - - table_options.filter_policy.reset( rocksdb::NewBloomFilterPolicy( bits_per_key, filter_policy[ USE_BLOCK_BASED_BUILDER ].template as< bool >() ) ); - } - else - { - table_options.filter_policy.reset( rocksdb::NewBloomFilterPolicy( bits_per_key ) ); - } - } - - o.table_factory.reset( ::rocksdb::NewBlockBasedTableFactory( table_options ) ); - } - } -}; - -fc::variant_object configuration::apply_configuration_overlay( const fc::variant& base, const fc::variant& overlay ) -{ - fc::mutable_variant_object config; - FC_ASSERT( base.is_object(), "Expected '${key}' configuration to be an object", - ("key", BASE) ); - - FC_ASSERT( overlay.is_object(), "Expected database overlay configuration to be an object" ); - - // Start with our base configuration - config = base.get_object(); - - // Iterate through the overlay overriding the base values - auto& overlay_obj = overlay.get_object(); - for ( auto it = overlay_obj.begin(); it != overlay_obj.end(); ++it ) - config[ it->key() ] = it->value(); - - return config; -} - -fc::variant_object configuration::retrieve_global_configuration( const fc::variant_object& obj ) -{ - fc::mutable_variant_object global_config; - - FC_ASSERT( obj[ GLOBAL ].is_object(), "Expected '${key}' configuration to be an object", - ("key", GLOBAL) ); - - global_config = obj[ GLOBAL ].get_object(); - return global_config; -} - -fc::variant_object configuration::retrieve_active_configuration( const fc::variant_object& obj, std::string type_name ) -{ - fc::mutable_variant_object active_config; - std::vector< std::string > split_v; - boost::split( split_v, type_name, boost::is_any_of( ":" ) ); - const auto index_name = *(split_v.rbegin()); - - FC_ASSERT( obj[ BASE ].is_object(), "Expected '${key}' configuration to be an object", - ("key", BASE) ); - - // We look to apply an index configuration overlay - if ( obj.find( index_name ) != obj.end() ) - active_config = apply_configuration_overlay( obj[ BASE ], obj[ index_name ] ); - else - active_config = obj[ BASE ].get_object(); - - return active_config; -} - -size_t configuration::get_object_count( const boost::any& cfg ) -{ - size_t object_count = 0; - - auto c = boost::any_cast< fc::variant >( cfg ); - FC_ASSERT( c.is_object(), "Expected database configuration to be an object" ); - auto& obj = c.get_object(); - - fc::variant_object global_config = retrieve_global_configuration( obj ); - - FC_ASSERT( global_config.contains( OBJECT_COUNT ), "Expected '${parent}' configuration to contain '${key}'", - ("parent", GLOBAL) - ("key", OBJECT_COUNT) ); - - FC_ASSERT( global_config[ OBJECT_COUNT ].is_uint64(), "Expected '${key}' to be an unsigned integer", - ("key", OBJECT_COUNT) ); - - object_count = global_config[ OBJECT_COUNT ].as< uint64_t >(); - - return object_count; -} - -bool configuration::gather_statistics( const boost::any& cfg ) -{ - bool statistics = false; - - auto c = boost::any_cast< fc::variant >( cfg ); - FC_ASSERT( c.is_object(), "Expected database configuration to be an object" ); - auto& obj = c.get_object(); - - fc::variant_object global_config = retrieve_global_configuration( obj ); - - FC_ASSERT( global_config.contains( STATISTICS ), "Expected '${parent}' configuration to contain '${key}'", - ("parent", GLOBAL) - ("key", STATISTICS) ); - - FC_ASSERT( global_config[ STATISTICS ].is_bool(), "Expected '${key}' to be a boolean value", - ("key", STATISTICS) ); - - statistics = global_config[ STATISTICS ].as< bool >(); - - return statistics; -} - -::rocksdb::Options configuration::get_options( const boost::any& cfg, std::string type_name ) -{ - ::rocksdb::Options opts; - - auto c = boost::any_cast< fc::variant >( cfg ); - FC_ASSERT( c.is_object(), "Expected database configuration to be an object" ); - auto& obj = c.get_object(); - - if ( global_shared_cache == nullptr ) - { - size_t capacity = 0; - int num_shard_bits = 0; - - fc::variant_object global_config = retrieve_global_configuration( obj ); - - FC_ASSERT( global_config.contains( SHARED_CACHE ), "Expected '${parent}' configuration to contain '${key}'", - ("parent", GLOBAL) - ("key", SHARED_CACHE) ); - - FC_ASSERT( global_config[ SHARED_CACHE ].is_object(), "Expected '${key}' to be an object", - ("key", SHARED_CACHE) ); - - auto& shared_cache_obj = global_config[ SHARED_CACHE ].get_object(); - - FC_ASSERT( shared_cache_obj.contains( CAPACITY ), "Expected '${parent}' configuration to contain '${key}'", - ("parent", SHARED_CACHE) - ("key", CAPACITY) ); - - FC_ASSERT( shared_cache_obj[ CAPACITY ].is_string(), "Expected '${key}' to be a string representation of an unsigned integer", - ("key", CAPACITY) ); - - capacity = shared_cache_obj[ CAPACITY ].as< uint64_t >(); - - if ( shared_cache_obj.contains( NUM_SHARD_BITS ) ) - { - FC_ASSERT( shared_cache_obj[ NUM_SHARD_BITS ].is_uint64(), "Expected '${key}' to be an unsigned integer", - ("key", NUM_SHARD_BITS) ); - - num_shard_bits = shared_cache_obj[ NUM_SHARD_BITS ].as< int >(); - - global_shared_cache = rocksdb::NewLRUCache( capacity, num_shard_bits ); - } - else - { - global_shared_cache = rocksdb::NewLRUCache( capacity ); - } - } - - if ( global_write_buffer_manager == nullptr ) - { - size_t write_buf_size = 0; - - fc::variant_object global_config = retrieve_global_configuration( obj ); - - FC_ASSERT( global_config.contains( WRITE_BUFFER_MANAGER ), "Expected '${parent}' configuration to contain '${key}'", - ("parent", GLOBAL) - ("key", WRITE_BUFFER_MANAGER) ); - - FC_ASSERT( global_config[ WRITE_BUFFER_MANAGER ].is_object(), "Expected '${key}' to be an object", - ("key", WRITE_BUFFER_MANAGER) ); - - auto& write_buffer_mgr_obj = global_config[ WRITE_BUFFER_MANAGER ].get_object(); - - FC_ASSERT( write_buffer_mgr_obj.contains( WRITE_BUFFER_SIZE ), "Expected '${parent}' configuration to contain '${key}'", - ("parent", WRITE_BUFFER_MANAGER) - ("key", WRITE_BUFFER_SIZE) ); - - FC_ASSERT( write_buffer_mgr_obj[ WRITE_BUFFER_SIZE ].is_string(), "Expected '${key}' to be a string representation of an unsigned integer", - ("key", WRITE_BUFFER_SIZE) ); - - write_buf_size = write_buffer_mgr_obj[ WRITE_BUFFER_SIZE ].as< uint64_t >(); - - global_write_buffer_manager = std::make_shared< ::rocksdb::WriteBufferManager >( write_buf_size, global_shared_cache ); - } - - // We assign the global write buffer manager to all databases - opts.write_buffer_manager = global_write_buffer_manager; - - fc::variant_object config = retrieve_active_configuration( obj, type_name ); - - for ( auto it = config.begin(); it != config.end(); ++it ) - { - try - { - if ( global_database_option_map.find( it->key() ) != global_database_option_map.end() ) - global_database_option_map[ it->key() ]( opts, it->value() ); - else - wlog( "Encountered an unknown database configuration option: ${key}", ("key",it->key()) ); - } - catch( ... ) - { - elog( "Error applying database option: ${key}", ("key", it->key()) ); - throw; - } - } - - return opts; -} - -} // mira diff --git a/libraries/mira/src/mira.cpp b/libraries/mira/src/mira.cpp deleted file mode 100644 index 31624c59e7..0000000000 --- a/libraries/mira/src/mira.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include -/* -namespace mira { namespace multi_index { - - void to_variant( const boost::tuples::null_type, fc::variant& vo ) {} - void from_variant( const fc::variant& vo, boost::tuples::null_type ) {} -} }*/ diff --git a/libraries/mira/test/CMakeLists.txt b/libraries/mira/test/CMakeLists.txt deleted file mode 100644 index 729bb5dfb6..0000000000 --- a/libraries/mira/test/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -if( ENABLE_MIRA ) - -file(GLOB UNIT_TEST_SOURCES "*.cpp") -file(GLOB UNIT_TEST_HEADERS "*.hpp") -add_executable( mira_test ${UNIT_TEST_SOURCES} ${UNIT_TEST_HEADERS} ) -target_link_libraries( mira_test mira fc chainbase hive_protocol hive_utilities ${PLATFORM_SPECIFIC_LIBS} ) - -endif() diff --git a/libraries/mira/test/test.cpp b/libraries/mira/test/test.cpp deleted file mode 100644 index f71b9e084b..0000000000 --- a/libraries/mira/test/test.cpp +++ /dev/null @@ -1,848 +0,0 @@ -#define BOOST_TEST_MODULE mira test - - -#include "test_objects.hpp" -#include "test_templates.hpp" - -#include -#include -#include - -using namespace mira; - -using mira::multi_index::multi_index_container; -using mira::multi_index::indexed_by; -using mira::multi_index::ordered_unique; -using mira::multi_index::tag; -using mira::multi_index::member; -using mira::multi_index::composite_key; -using mira::multi_index::composite_key_compare; -using mira::multi_index::const_mem_fun; - -struct mira_fixture { - boost::filesystem::path tmp; - chainbase::database db; - mira_fixture() - { - tmp = boost::filesystem::current_path() / boost::filesystem::unique_path(); - db.open( tmp, 0, 1024*1024*8, hive::utilities::default_database_configuration() ); - } - - ~mira_fixture() - { - chainbase::bfs::remove_all( tmp ); - } -}; - -BOOST_FIXTURE_TEST_SUITE( mira_tests, mira_fixture ) - -BOOST_AUTO_TEST_CASE( sanity_tests ) -{ - try - { - db.add_index< book_index >(); - - BOOST_TEST_MESSAGE( "Creating book" ); - const auto& new_book = - db.create( []( book& b ) - { - b.a = 3; - b.b = 4; - }); - - BOOST_REQUIRE( new_book.get_id() == book_id_type() ); - BOOST_REQUIRE( new_book.a == 3 ); - BOOST_REQUIRE( new_book.b == 4 ); - - try - { - db.create( []( book& b ) - { - b.a = 3; - b.b = 5; - }); - - BOOST_REQUIRE( false ); - } catch( ... ) {} - - db.create( []( book& b ) - { - b.a = 4; - b.b = 5; - }); - - db.create( []( book& b ) - { - b.a = 2; - b.b = 1; - }); - - const auto& book_idx = db.get_index< book_index, by_id >(); - auto itr = book_idx.begin(); - - BOOST_REQUIRE( itr != book_idx.end() ); - - { - const auto& tmp_book = *itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 3 ); - BOOST_REQUIRE( tmp_book.b == 4 ); - } - - ++itr; - - { - const auto& tmp_book = *itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++itr; - - { - const auto& tmp_book = *itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++itr; - - BOOST_REQUIRE( itr == book_idx.end() ); - - itr = book_idx.end(); - - BOOST_REQUIRE( itr == book_idx.end() ); - - --itr; - - { - const auto& tmp_book = *itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - --itr; - - { - const auto& tmp_book = *itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - --itr; - - { - const auto& tmp_book = *itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 3 ); - BOOST_REQUIRE( tmp_book.b == 4 ); - } - - const auto& book_by_a_idx = db.get_index< book_index, by_a >(); - auto a_itr = book_by_a_idx.begin(); - - { - const auto& tmp_book = *a_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++a_itr; - - { - const auto& tmp_book = *a_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 3 ); - BOOST_REQUIRE( tmp_book.b == 4 ); - } - - ++a_itr; - - { - const auto& tmp_book = *a_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - { - const auto& tmp_book = *(book_by_a_idx.lower_bound( 3 )); - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 3 ); - BOOST_REQUIRE( tmp_book.b == 4 ); - } - - BOOST_REQUIRE( book_by_a_idx.lower_bound( 5 ) == book_by_a_idx.end() ); - - { - const auto& tmp_book = *(book_by_a_idx.upper_bound( 3 )); - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - BOOST_REQUIRE( book_by_a_idx.upper_bound( 5 ) == book_by_a_idx.end() ); - - { - const auto& tmp_book = db.get< book, by_id >( 1 ); - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - { - const auto* book_ptr = db.find< book, by_a >( 4 ); - - BOOST_REQUIRE( book_ptr->get_id() == book::id_type(1) ); - BOOST_REQUIRE( book_ptr->a == 4 ); - BOOST_REQUIRE( book_ptr->b == 5 ); - } - - bool is_found = db.find< book, by_a >( 10 ) != nullptr; - - BOOST_REQUIRE( !is_found ); - - const auto& book_by_b_idx = db.get_index< book_index, by_b >(); - auto b_itr = book_by_b_idx.begin(); - - BOOST_REQUIRE( b_itr != book_by_b_idx.end() ); - - { - const auto& tmp_book = *b_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++b_itr; - - { - const auto& tmp_book = *b_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 3 ); - BOOST_REQUIRE( tmp_book.b == 4 ); - } - - ++b_itr; - - { - const auto& tmp_book = *b_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++b_itr; - - BOOST_REQUIRE( b_itr == book_by_b_idx.end() ); - - const auto& book_by_b = db.get< book, by_b >( boost::make_tuple( 5, 4 ) ); - - BOOST_REQUIRE( book_by_b.get_id() == book::id_type(1) ); - BOOST_REQUIRE( book_by_b.a == 4 ); - BOOST_REQUIRE( book_by_b.b == 5 ); - - b_itr = book_by_b_idx.lower_bound( 10 ); - - { - const auto& tmp_book = *b_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - const auto& book_by_sum_idx = db.get_index< book_index, by_sum >(); - auto by_sum_itr = book_by_sum_idx.begin(); - - { - const auto& tmp_book = *by_sum_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++by_sum_itr; - - { - const auto& tmp_book = *by_sum_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 3 ); - BOOST_REQUIRE( tmp_book.b == 4 ); - } - - ++by_sum_itr; - - { - const auto& tmp_book = *by_sum_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++by_sum_itr; - - BOOST_REQUIRE( by_sum_itr == book_by_sum_idx.end() ); - - const auto& book_by_id = db.get< book, by_id >( 0 ); - const auto& book_by_a = db.get< book, by_a >( 3 ); - - BOOST_REQUIRE( &book_by_id == &book_by_a ); - - db.modify( db.get< book, by_id >( 0 ), []( book& b ) - { - b.a = 10; - b.b = 5; - }); - - { - const auto& tmp_book = db.get< book, by_id >( 0 ); - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 10 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - try - { - // Failure due to collision on 'a' - db.modify( db.get< book, by_id >( 0 ), []( book& b ) - { - b.a = 4; - b.b = 10; - }); - BOOST_REQUIRE( false ); - } - catch( ... ) - { - const auto& tmp_book = db.get< book, by_id >( 0 ); - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 10 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - try - { - // Failure due to collision on 'sum' - db.modify( db.get< book, by_id >( 0 ), []( book& b ) - { - b.a = 6; - b.b = 3; - }); - BOOST_REQUIRE( false ); - } - catch( ... ) - { - const auto& tmp_book = db.get< book, by_id >( 0 ); - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 10 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - a_itr = book_by_a_idx.begin(); - - BOOST_REQUIRE( a_itr != book_by_a_idx.end() ); - - { - const auto& tmp_book = *a_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++a_itr; - - { - const auto& tmp_book = *a_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++a_itr; - - { - const auto& tmp_book = *a_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 10 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++a_itr; - - BOOST_REQUIRE( a_itr == book_by_a_idx.end() ); - - b_itr = book_by_b_idx.begin(); - - BOOST_REQUIRE( b_itr != book_by_b_idx.end() ); - - { - const auto& tmp_book = *b_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - - } - - ++b_itr; - - { - const auto& tmp_book = *b_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 10 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++b_itr; - - { - const auto& tmp_book = *b_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++b_itr; - - BOOST_REQUIRE( b_itr == book_by_b_idx.end() ); - - b_itr = book_by_b_idx.lower_bound( boost::make_tuple( 5, 5 ) ); - - { - const auto& tmp_book = *b_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 10 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - by_sum_itr = book_by_sum_idx.begin(); - - { - const auto& tmp_book = *by_sum_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++by_sum_itr; - - { - const auto& tmp_book = *by_sum_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++by_sum_itr; - - { - const auto& tmp_book = *by_sum_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book_id_type() ); - BOOST_REQUIRE( tmp_book.a == 10 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++by_sum_itr; - - BOOST_REQUIRE( by_sum_itr == book_by_sum_idx.end() ); - - db.remove( db.get< book, by_id >( 0 ) ); - - is_found = db.find< book, by_id >( 0 ) != nullptr; - - BOOST_REQUIRE( !is_found ); - - itr = book_idx.begin(); - - BOOST_REQUIRE( itr != book_idx.end() ); - - { - const auto& tmp_book = *itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++itr; - - { - const auto& tmp_book = *itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++itr; - - BOOST_REQUIRE( itr == book_idx.end() ); - - a_itr = book_by_a_idx.begin(); - - BOOST_REQUIRE( a_itr != book_by_a_idx.end() ); - - { - const auto& tmp_book = *a_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(2) ); - BOOST_REQUIRE( tmp_book.a == 2 ); - BOOST_REQUIRE( tmp_book.b == 1 ); - } - - ++a_itr; - - { - const auto& tmp_book = *a_itr; - - BOOST_REQUIRE( tmp_book.get_id() == book::id_type(1) ); - BOOST_REQUIRE( tmp_book.a == 4 ); - BOOST_REQUIRE( tmp_book.b == 5 ); - } - - ++a_itr; - - BOOST_REQUIRE( a_itr == book_by_a_idx.end() ); - - } - FC_LOG_AND_RETHROW(); -} - -BOOST_AUTO_TEST_CASE( single_index_test ) -{ - try - { - db.add_index< single_index_index >(); - - db.create< single_index_object >( [&]( single_index_object& ){} ); - - const auto& sio = db.get( single_index_id_type() ); - - BOOST_REQUIRE( sio.get_id() == single_index_id_type() ); - } - FC_LOG_AND_RETHROW(); -} - -BOOST_AUTO_TEST_CASE( variable_length_key_test ) -{ - try - { - db.add_index< account_index >(); - - const auto& acc_by_name_idx = db.get_index< account_index, by_name >(); - auto itr = acc_by_name_idx.begin(); - - BOOST_REQUIRE( itr == acc_by_name_idx.end() ); - - db.create< account_object >( "alice" ); - db.create< account_object >( "bob" ); - db.create< account_object >( "charlie" ); - - itr = acc_by_name_idx.begin(); - - BOOST_REQUIRE( itr->name == "alice" ); - - ++itr; - - BOOST_REQUIRE( itr->name == "bob" ); - - ++itr; - - BOOST_REQUIRE( itr->name == "charlie" ); - - ++itr; - - BOOST_REQUIRE( itr == acc_by_name_idx.end() ); - - itr = acc_by_name_idx.lower_bound( "archibald" ); - - BOOST_REQUIRE( itr->name == "bob" ); - } - FC_LOG_AND_RETHROW(); -} - -BOOST_AUTO_TEST_CASE( sanity_modify_test ) -{ - try - { - db.add_index< book_index >(); - - const auto& b1 = db.create( []( book& b ) - { - b.a = 1; - b.b = 2; - } ); - - const auto& b2 = db.create( []( book& b ) - { - b.a = 2; - b.b = 3; - } ); - - const auto& b3 = db.create( []( book& b ) - { - b.a = 4; - b.b = 5; - } ); - - BOOST_REQUIRE( b1.a == 1 ); - BOOST_REQUIRE( b1.b == 2 ); - BOOST_REQUIRE( b1.sum() == 3 ); - - BOOST_REQUIRE( b2.a == 2 ); - BOOST_REQUIRE( b2.b == 3 ); - BOOST_REQUIRE( b2.sum() == 5 ); - - BOOST_REQUIRE( b3.a == 4 ); - BOOST_REQUIRE( b3.b == 5 ); - BOOST_REQUIRE( b3.sum() == 9 ); - - db.modify( b2, [] ( book& b ) - { - b.a = 10; - b.b = 20; - } ); - - BOOST_REQUIRE( b2.a == 10 ); - BOOST_REQUIRE( b2.b == 20 ); - BOOST_REQUIRE( b2.sum() == 30 ); - - auto& idx_by_a = db.get_index< book_index, by_a >(); - - auto bb = idx_by_a.end(); - --bb; - - BOOST_REQUIRE( bb->a == 10 ); - BOOST_REQUIRE( bb->b == 20 ); - BOOST_REQUIRE( bb->sum() == 30 ); - BOOST_REQUIRE( &*bb == &b2 ); - - auto& idx_by_sum = db.get_index< book_index, by_sum >(); - auto bb2 = idx_by_sum.end(); - --bb2; - - BOOST_REQUIRE( bb2->a == 10 ); - BOOST_REQUIRE( bb2->b == 20 ); - BOOST_REQUIRE( bb2->sum() == 30 ); - BOOST_REQUIRE( &*bb2 == &b2 ); - } - FC_LOG_AND_RETHROW(); -} - -BOOST_AUTO_TEST_CASE( range_test ) -{ - try - { - db.add_index< test_object3_index >(); - - for ( uint32_t i = 0; i < 10; i++ ) - { - for ( uint32_t j = 0; j < 10; j++ ) - { - db.create< test_object3 >( [=] ( test_object3& o ) - { - o.val = i; - o.val2 = j; - o.val3 = i + j; - } ); - } - } - - const auto& idx = db.get_index< test_object3_index, composite_ordered_idx3a >(); - - auto er = idx.equal_range( 5 ); - - BOOST_REQUIRE( er.first->val == 5 ); - BOOST_REQUIRE( er.first->val2 == 0 ); - - BOOST_REQUIRE( er.second->val == 6 ); - BOOST_REQUIRE( er.second->val2 == 0 ); - - auto er2 = idx.equal_range( 9 ); - - BOOST_REQUIRE( er2.first->val == 9 ); - BOOST_REQUIRE( er2.first->val2 == 0 ); - - BOOST_REQUIRE( er2.second == idx.end() ); - } - FC_LOG_AND_RETHROW(); -} - -BOOST_AUTO_TEST_CASE( bounds_test ) -{ - try - { - db.add_index< test_object3_index >(); - - for ( uint32_t i = 0; i < 10; i++ ) - { - for ( uint32_t j = 0; j < 10; j++ ) - { - db.create< test_object3 >( [=] ( test_object3& o ) - { - o.val = i; - o.val2 = j; - o.val3 = i + j; - } ); - } - } - - const auto& idx = db.get_index< test_object3_index, composite_ordered_idx3a >(); - - auto upper_bound_not_found = idx.upper_bound( 10 ); - BOOST_REQUIRE( upper_bound_not_found == idx.end() ); - - auto lower_bound_not_found = idx.lower_bound( 10 ); - BOOST_REQUIRE( lower_bound_not_found == idx.end() ); - - auto composite_lower_bound = idx.lower_bound( boost::make_tuple( 3, 1 ) ); - - BOOST_REQUIRE( composite_lower_bound->val == 3 ); - BOOST_REQUIRE( composite_lower_bound->val2 == 1 ); - - auto composite_upper_bound = idx.upper_bound( boost::make_tuple( 3, 5 ) ); - - BOOST_REQUIRE( composite_upper_bound->val == 3 ); - BOOST_REQUIRE( composite_upper_bound->val2 == 6 ); - - auto lower_iter = idx.lower_bound( 5 ); - - BOOST_REQUIRE( lower_iter->val == 5 ); - BOOST_REQUIRE( lower_iter->val2 == 0 ); - - auto upper_iter = idx.upper_bound( 5 ); - - BOOST_REQUIRE( upper_iter->val == 6 ); - BOOST_REQUIRE( upper_iter->val2 == 0 ); - } - FC_LOG_AND_RETHROW(); -} - -BOOST_AUTO_TEST_CASE( basic_tests ) -{ - db.add_index< test_object_index >(); - db.add_index< test_object2_index >(); - db.add_index< test_object3_index >(); - - auto c1 = []( test_object& obj ) { obj.name = "_name"; }; - auto c1b = []( test_object2& obj ) {}; - auto c1c = []( test_object3& obj ) { obj.val2 = 5; obj.val3 = 5; }; - - basic_test< test_object_index, test_object, by_id >( { 0, 1, 2, 3, 4, 5 }, c1, db ); - basic_test< test_object2_index, test_object2, by_id >( { 0, 1, 2 }, c1b, db ); - basic_test< test_object3_index, test_object3, by_id >( { 0, 1, 2, 3, 4 }, c1c, db ); -} - -BOOST_AUTO_TEST_CASE( insert_remove_tests ) -{ - db.add_index< test_object_index >(); - db.add_index< test_object2_index >(); - db.add_index< test_object3_index >(); - - auto c1 = []( test_object& obj ) { obj.name = "_name"; }; - auto c1b = []( test_object2& obj ) {}; - auto c1c = []( test_object3& obj ) { obj.val2 = 7; obj.val3 = obj.val2 + 1; }; - - insert_remove_test< test_object_index, test_object, by_id >( { 0, 1, 2, 3, 4, 5, 6, 7 }, c1, db ); - insert_remove_test< test_object2_index, test_object2, by_id >( { 0, 1, 2, 3, 4, 5, 6, 7 }, c1b, db ); - insert_remove_test< test_object3_index, test_object3, by_id >( { 0, 1, 2, 3 }, c1c, db ); -} - -BOOST_AUTO_TEST_CASE( insert_remove_collision_tests ) -{ - db.add_index< test_object_index >(); - db.add_index< test_object2_index >(); - db.add_index< test_object3_index >(); - - auto c1 = []( test_object& obj ) { obj.set_id( 0 ); obj.name = "_name7"; obj.val = 7; }; - auto c2 = []( test_object& obj ) { obj.set_id( 0 ); obj.name = "_name8"; obj.val = 8; }; - auto c3 = []( test_object& obj ) { obj.set_id( 0 ); obj.name = "the_same_name"; obj.val = 7; }; - auto c4 = []( test_object& obj ) { obj.set_id( 1 ); obj.name = "the_same_name"; obj.val = 7; }; - - auto c1b = []( test_object2& obj ) { obj.set_id( 0 ); obj.val = 7; }; - auto c2b = []( test_object2& obj ) { obj.set_id( 0 ); obj.val = 8; }; - auto c3b = []( test_object2& obj ) { obj.set_id( 6 ); obj.val = 7; }; - auto c4b = []( test_object2& obj ) { obj.set_id( 6 ); obj.val = 7; }; - - auto c1c = []( test_object3& obj ) { obj.set_id( 0 ); obj.val = 20; obj.val2 = 20; }; - auto c2c = []( test_object3& obj ) { obj.set_id( 1 ); obj.val = 20; obj.val2 = 20; }; - auto c3c = []( test_object3& obj ) { obj.set_id( 2 ); obj.val = 30; obj.val3 = 30; }; - auto c4c = []( test_object3& obj ) { obj.set_id( 3 ); obj.val = 30; obj.val3 = 30; }; - - insert_remove_collision_test< test_object_index, test_object, by_id >( {}, c1, c2, c3, c4, db ); - insert_remove_collision_test< test_object2_index, test_object2, by_id >( {}, c1b, c2b, c3b, c4b, db ); - insert_remove_collision_test< test_object3_index, test_object3, by_id >( {}, c1c, c2c, c3c, c4c, db ); -} - -BOOST_AUTO_TEST_CASE( modify_tests ) -{ - db.add_index< test_object_index >(); - db.add_index< test_object2_index >(); - db.add_index< test_object3_index >(); - - auto c1 = []( test_object& obj ) { obj.name = "_name"; }; - auto c2 = []( test_object& obj ){ obj.name = "new_name"; }; - auto c3 = []( const test_object& obj ){ BOOST_REQUIRE( obj.name == "new_name" ); }; - auto c4 = []( const test_object& obj ){ BOOST_REQUIRE( obj.val == size_t( obj.get_id() ) + 100 ); }; - auto c5 = []( bool result ){ BOOST_REQUIRE( result == false ); }; - - auto c1b = []( test_object2& obj ) { obj.val = 889; }; - auto c2b = []( test_object2& obj ){ obj.val = 2889; }; - auto c3b = []( const test_object2& obj ){ BOOST_REQUIRE( obj.val == 2889 ); }; - auto c4b = []( const test_object2& obj ){ /*empty*/ }; - auto c5b = []( bool result ){ BOOST_REQUIRE( result == true ); }; - - modify_test< test_object_index, test_object, by_id >( { 0, 1, 2, 3 }, c1, c2, c3, c4, c5, db ); - modify_test< test_object2_index, test_object2, by_id >( { 0, 1, 2, 3, 4, 5 }, c1b, c2b, c3b, c4b, c5b, db ); -} - -BOOST_AUTO_TEST_CASE( misc_tests ) -{ - db.add_index< test_object_index >(); - - misc_test< test_object_index, test_object, by_id, composited_ordered_idx >( { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, db ); -} - -BOOST_AUTO_TEST_CASE( misc_tests3 ) -{ - db.add_index< test_object3_index >(); - - misc_test3< test_object3_index, test_object3, by_id, composite_ordered_idx3a, composite_ordered_idx3b >( { 0, 1, 2 }, db ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/libraries/mira/test/test_objects.hpp b/libraries/mira/test/test_objects.hpp deleted file mode 100644 index 5c542495d9..0000000000 --- a/libraries/mira/test/test_objects.hpp +++ /dev/null @@ -1,257 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -enum test_object_type -{ - book_object_type, - single_index_object_type, - test_object_type, - test_object2_type, - test_object3_type, - account_object_type -}; - -class book : public chainbase::object< book_object_type, book > -{ - CHAINBASE_OBJECT( book ); - -public: - CHAINBASE_DEFAULT_CONSTRUCTOR( book ) - - int a = 0; - int b = 1; - - int sum()const { return a + b; } - -}; -typedef chainbase::oid_ref< book > book_id_type; - - -namespace fc { -namespace raw { -template -inline void pack(Stream& s, const book&) - { - } - -template -inline void unpack(Stream& s, book& id, uint32_t depth = 0) - { - } -} -} - - -using chainbase::by_id; -struct by_a; -struct by_b; -struct by_sum; - -typedef mira::multi_index_adapter< - book, - mira::multi_index::indexed_by< - mira::multi_index::ordered_unique< mira::multi_index::tag< by_id >, - mira::multi_index::const_mem_fun< book, book::id_type, &book::get_id > >, - mira::multi_index::ordered_unique< mira::multi_index::tag< by_a >, - mira::multi_index::member< book, int, &book::a > >, - mira::multi_index::ordered_unique< mira::multi_index::tag< by_b >, - mira::multi_index::composite_key< book, - mira::multi_index::member< book, int, &book::b >, - mira::multi_index::member< book, int, &book::a > - >, - mira::multi_index::composite_key_compare< std::greater< int >, std::less< int > > - >, - mira::multi_index::ordered_unique< mira::multi_index::tag< by_sum >, mira::multi_index::const_mem_fun< book, int, &book::sum > > - >, - chainbase::allocator< book > -> book_index; - -class single_index_object : public chainbase::object< single_index_object_type, single_index_object > -{ - CHAINBASE_OBJECT( single_index_object ); - -public: - CHAINBASE_DEFAULT_CONSTRUCTOR( single_index_object ) - -}; -typedef chainbase::oid_ref< single_index_object > single_index_id_type; - -typedef mira::multi_index_adapter< - single_index_object, - mira::multi_index::indexed_by< - mira::multi_index::ordered_unique< mira::multi_index::tag< by_id >, - mira::multi_index::const_mem_fun< single_index_object, single_index_object::id_type, &single_index_object::get_id > > - >, - chainbase::allocator< single_index_object > -> single_index_index; - -class test_object : public chainbase::object< test_object_type, test_object > -{ - CHAINBASE_OBJECT( test_object ); - -public: - CHAINBASE_DEFAULT_CONSTRUCTOR( test_object, (name) ) - void set_id( int _id ) { id = id_type( _id ); } - - uint32_t val = 0; - std::string name; - -}; -typedef chainbase::oid_ref< test_object > test_id_type; - -struct composited_ordered_idx; - -typedef mira::multi_index_adapter< - test_object, - mira::multi_index::indexed_by< - mira::multi_index::ordered_unique< mira::multi_index::tag< by_id >, - mira::multi_index::const_mem_fun< test_object, test_object::id_type, &test_object::get_id > >, - mira::multi_index::ordered_unique< mira::multi_index::tag< composited_ordered_idx >, - mira::multi_index::composite_key< test_object, - mira::multi_index::member< test_object, std::string, &test_object::name >, - mira::multi_index::member< test_object, uint32_t, &test_object::val > - > - > - >, - chainbase::allocator< test_object > -> test_object_index; - -class test_object2 : public chainbase::object< test_object2_type, test_object2 > -{ - CHAINBASE_OBJECT( test_object2 ); - -public: - CHAINBASE_DEFAULT_CONSTRUCTOR( test_object2 ) - void set_id( int _id ) { id = id_type( _id ); } - - uint32_t val = 0; - -}; -typedef chainbase::oid_ref< test_object2 > test2_id_type; - -struct composite_ordered_idx2; - -typedef mira::multi_index_adapter< - test_object2, - mira::multi_index::indexed_by< - mira::multi_index::ordered_unique< mira::multi_index::tag< by_id >, - mira::multi_index::const_mem_fun< test_object2, test_object2::id_type, &test_object2::get_id > >, - mira::multi_index::ordered_unique< mira::multi_index::tag< composite_ordered_idx2 >, - mira::multi_index::composite_key< test_object2, - mira::multi_index::member< test_object2, uint32_t, &test_object2::val >, - mira::multi_index::const_mem_fun< test_object2, test_object2::id_type, &test_object2::get_id > - > - > - >, - chainbase::allocator< test_object2 > -> test_object2_index; - -class test_object3 : public chainbase::object< test_object3_type, test_object3 > -{ - CHAINBASE_OBJECT( test_object3 ); - -public: - CHAINBASE_DEFAULT_CONSTRUCTOR( test_object3 ) - void set_id( int _id ) { id = id_type( _id ); } - - uint32_t val = 0; - uint32_t val2 = 0; - uint32_t val3 = 0; - -}; - -typedef chainbase::oid_ref< test_object3 > test3_id_type; - -struct composite_ordered_idx3a; -struct composite_ordered_idx3b; - -typedef mira::multi_index_adapter< - test_object3, - mira::multi_index::indexed_by< - mira::multi_index::ordered_unique< mira::multi_index::tag< by_id >, - mira::multi_index::const_mem_fun< test_object3, test_object3::id_type, &test_object3::get_id > >, - mira::multi_index::ordered_unique< mira::multi_index::tag< composite_ordered_idx3a >, - mira::multi_index::composite_key< test_object3, - mira::multi_index::member< test_object3, uint32_t, &test_object3::val >, - mira::multi_index::member< test_object3, uint32_t, &test_object3::val2 > - > - >, - mira::multi_index::ordered_unique< mira::multi_index::tag< composite_ordered_idx3b >, - mira::multi_index::composite_key< test_object3, - mira::multi_index::member< test_object3, uint32_t, &test_object3::val >, - mira::multi_index::member< test_object3, uint32_t, &test_object3::val3 > - > - > - >, - chainbase::allocator< test_object3 > -> test_object3_index; - - -typedef hive::protocol::fixed_string<16> account_name_type; - -class account_object : public chainbase::object< account_object_type, account_object > -{ - CHAINBASE_OBJECT( account_object ); - -public: - template< typename Allocator > - account_object( Allocator&& a, uint64_t _id, const account_name_type& _name ) - : id( _id ), name( _name ) - {} - - account_name_type name; - - CHAINBASE_UNPACK_CONSTRUCTOR( account_object ); -}; -typedef chainbase::oid_ref< account_object > account_id_type; - -struct by_name; - -typedef mira::multi_index_adapter< - account_object, - mira::multi_index::indexed_by< - mira::multi_index::ordered_unique< mira::multi_index::tag< by_id >, - mira::multi_index::const_mem_fun< account_object, account_object::id_type, &account_object::get_id > >, - mira::multi_index::ordered_unique< mira::multi_index::tag< by_name >, - mira::multi_index::member< account_object, account_name_type, &account_object::name > > - >, - chainbase::allocator< account_object > -> account_index; - -FC_REFLECT( book::id_type, (_id) ) -FC_REFLECT( book, (id)(a)(b) ) -CHAINBASE_SET_INDEX_TYPE( book, book_index ) - -FC_REFLECT( single_index_object::id_type, (_id) ) -FC_REFLECT( single_index_object, (id) ) -CHAINBASE_SET_INDEX_TYPE( single_index_object, single_index_index ) - -FC_REFLECT( test_object::id_type, (_id) ) -FC_REFLECT( test_object, (id)(val)(name) ) -CHAINBASE_SET_INDEX_TYPE( test_object, test_object_index ) - -FC_REFLECT( test_object2::id_type, (_id) ) -FC_REFLECT( test_object2, (id)(val) ) -CHAINBASE_SET_INDEX_TYPE( test_object2, test_object2_index ) - -FC_REFLECT( test_object3::id_type, (_id) ) -FC_REFLECT( test_object3, (id)(val)(val2)(val3) ) -CHAINBASE_SET_INDEX_TYPE( test_object3, test_object3_index ) - -FC_REFLECT( account_object::id_type, (_id) ) -FC_REFLECT( account_object, (id)(name) ) -CHAINBASE_SET_INDEX_TYPE( account_object, account_index ) diff --git a/libraries/mira/test/test_templates.hpp b/libraries/mira/test/test_templates.hpp deleted file mode 100644 index 2f135750b2..0000000000 --- a/libraries/mira/test/test_templates.hpp +++ /dev/null @@ -1,454 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -template < typename IndexType, typename Object, typename Order > -void basic_test( const std::vector< uint64_t >& v, - std::function< void( Object& ) > call, - chainbase::database& db ) -{ - const auto& c = db.get_index< IndexType, Order >(); - - BOOST_TEST_MESSAGE( "Creating `v.size()` objects" ); - for( const auto& item : v ) - { - db.create< Object >( [&] ( Object& o ) - { - o.set_id( item ); - call( o ); - o.val = 100 - item; - } ); - } - - BOOST_REQUIRE( v.size() == c.size() ); - - BOOST_TEST_MESSAGE( "Removing all objects" ); - { - auto it = c.begin(); - while ( it != c.end() ) - { - auto to_remove = it; ++it; - db.remove( *to_remove ); - } - } - - BOOST_REQUIRE( c.size() == 0 ); - - BOOST_TEST_MESSAGE( "Creating 1 object" ); - db.create< Object >( [&] ( Object& o ) - { - o.set_id( 0 ); - call( o ); - o.val = 888; - } ); - - BOOST_TEST_MESSAGE( "Modyfing 1 object" ); - db.modify( *( c.begin() ), [] ( Object& o ) - { - o.val = o.val + 1; - } ); - - BOOST_REQUIRE( c.size() == 1 ); - - BOOST_TEST_MESSAGE( "Removing 1 object" ); - db.remove( *( c.begin() ) ); - - BOOST_REQUIRE( c.size() == 0 ); - - BOOST_TEST_MESSAGE( "Creating `v.size()` objects" ); - for( const auto& item : v ) - { - db.create< Object >( [&] ( Object& o ) - { - o.set_id( item ); - call( o ); - o.val = 100 - item; - } ); - } - - BOOST_REQUIRE( v.size() == c.size() ); - - BOOST_TEST_MESSAGE( "Removing all objects one by one" ); - { - auto it = c.begin(); - while (it != c.end()) - { - auto to_remove = it; ++it; - db.remove( *to_remove ); - } - } - - BOOST_REQUIRE( c.size() == 0 ); -} - -template < typename IndexType, typename Object, typename Order > -void insert_remove_test( const std::vector< uint64_t >& v, - std::function< void( Object& ) > call, - chainbase::database& db ) -{ - const auto& c = db.get_index< IndexType, Order >(); - - int64_t cnt = 0; - - BOOST_TEST_MESSAGE( "Creating `v.size()` objects" ); - for( const auto& item : v ) - { - db.create< Object >( [&] ( Object& o ) - { - o.set_id( item ); - call( o ); - o.val = item; - } ); - - ++cnt; - } - BOOST_REQUIRE( v.size() == c.size() ); - - BOOST_TEST_MESSAGE( "Removing objects one by one. Every object is removed by erase method" ); - for( int64_t i = 0; i < cnt; ++i ) - { - auto found = c.find( i ); - BOOST_REQUIRE( found != c.end() ); - - db.remove( *found ); - BOOST_REQUIRE( v.size() - i - 1 == c.size() ); - } - BOOST_REQUIRE( c.size() == 0 ); - - BOOST_TEST_MESSAGE( "Creating 2 objects" ); - cnt = 0; - for( const auto& item : v ) - { - db.create< Object >( [&] ( Object& o ) - { - o.set_id( item ); - call( o ); - o.val = cnt; - } ); - - if( ++cnt == 2 ) - break; - } - BOOST_REQUIRE( c.size() == 2 ); - - BOOST_TEST_MESSAGE( "Removing first object" ); - db.remove( *( c.begin() ) ); - - BOOST_REQUIRE( c.size() == 1 ); - - BOOST_TEST_MESSAGE( "Adding object with id_key = 0" ); - db.create< Object >( [&] ( Object& o ) - { - o.set_id( v[0] ); - call( o ); - o.val = 100; - } ); - - BOOST_REQUIRE( c.size() == 2 ); - - BOOST_TEST_MESSAGE( "Remove second object" ); - auto found = c.end(); - --found; - //auto first = std::prev( found, 1 ); // This crashes - /* This alternative works */ - auto first = found; - --first; - - db.remove( *found ); - BOOST_REQUIRE( c.size() == 1 ); - - db.remove( *first ); - BOOST_REQUIRE( c.size() == 0 ); -} - -template < typename IndexType, typename Object, typename Order > -void insert_remove_collision_test( const std::vector< uint64_t >& v, - std::function< void( Object& ) > call1, - std::function< void( Object& ) > call2, - std::function< void( Object& ) > call3, - std::function< void( Object& ) > call4, - chainbase::database& db ) -{ - const auto& c = db.get_index< IndexType, Order >(); - - BOOST_TEST_MESSAGE( "Adding 2 objects with collision - the same id" ); - - db.create< Object >( call1 ); - BOOST_CHECK_THROW( db.create< Object >( call2 ), std::logic_error ); - - BOOST_REQUIRE( c.size() == 1 ); - - BOOST_TEST_MESSAGE( "Removing object and adding 2 objects with collision - the same name+val" ); - db.remove( *( c.begin() ) ); - - db.create< Object >( call3 ); - BOOST_CHECK_THROW( db.create< Object >( call4 ), std::logic_error ); - BOOST_REQUIRE( c.size() == 1 ); -} - -template < typename IndexType, typename Object, typename Order > -void modify_test( const std::vector< uint64_t >& v, - std::function< void( Object& ) > call1, - std::function< void( Object& ) > call2, - std::function< void( const Object& ) > call3, - std::function< void( const Object& ) > call4, - std::function< void( bool ) > call5, - chainbase::database& db ) -{ - const auto& c = db.get_index< IndexType, Order >(); - - BOOST_TEST_MESSAGE( "Creating `v.size()` objects" ); - for( const auto& item : v ) - { - db.create< Object >( [&] ( Object& o ) - { - o.set_id( item ); - call1( o ); - o.val = item + 100; - } ); - } - BOOST_REQUIRE( v.size() == c.size() ); - - BOOST_TEST_MESSAGE( "Modifying key in all objects" ); - auto it = c.begin(); - auto end = c.end(); - for( ; it != end; ++it ) - db.modify( *it, call2 ); - - BOOST_REQUIRE( v.size() == c.size() ); - int32_t cnt = 0; - for( const auto& item : c ) - { - BOOST_REQUIRE( size_t( item.get_id() ) == v[ cnt++ ] ); - db.modify( item, call3 ); - db.modify( item, call4 ); - } - - BOOST_TEST_MESSAGE( "Modifying 'val' key in order to create collisions for all objects" ); - uint32_t first_val = c.begin()->val; - it = std::next( c.begin(), 1 ); - for( ; it != end; ++it ) - { - bool result = true; - try - { - db.modify( *it, [&] ( Object& o ) - { - o.val = first_val; - } ); - } - catch ( const std::logic_error& e ) - { - result = false; - } - - call5( result ); - } -} - -template < typename IndexType, typename Object, typename SimpleIndex, typename ComplexIndex > -void misc_test( const std::vector< uint64_t >& v, chainbase::database& db ) -{ - const auto& c = db.get_index< IndexType, SimpleIndex >(); - - BOOST_TEST_MESSAGE( "Creating `v.size()` objects" ); - for( const auto& item : v ) - { - db.create< Object >( [&] ( Object& o ) - { - o.set_id( item ); - o.name = "any_name"; - o.val = item + 200; - } ); - } - BOOST_REQUIRE( v.size() == c.size() ); - - BOOST_TEST_MESSAGE( "Reading all elements" ); - uint32_t cnt = 0; - for( const auto& item : c ) - { - BOOST_REQUIRE( size_t( item.get_id() ) == v[ cnt++ ] ); - BOOST_REQUIRE( item.name == "any_name" ); - BOOST_REQUIRE( item.val == size_t( item.get_id() ) + 200 ); - } - - BOOST_TEST_MESSAGE( "Removing 2 objects: first and last" ); - db.remove ( *( c.begin() ) ); - { - auto last = c.end(); - --last; - db.remove( *last ); - } - - BOOST_REQUIRE( v.size() - 2 == c.size() ); - - //Modyfing key `name` in one object. - db.modify( *( c.begin() ), [] ( Object& o ) { o.name = "completely_different_name"; } ); - auto it1 = c.begin(); - BOOST_REQUIRE( it1->name == "completely_different_name" ); - uint32_t val1 = it1->val; - - //Creating collision in two objects: [1] and [2] - auto it2 = it1; - it2++; - BOOST_CHECK_THROW( db.modify( *it2, [ val1 ]( Object& obj ){ obj.name = "completely_different_name"; obj.val = val1;} ), std::logic_error ); - - //Removing object [1]. - it1 = c.begin(); - it1++; - - db.remove( *it1 ); - BOOST_REQUIRE( v.size() - 3 == c.size() ); - - //Removing all remaining objects - auto it_erase = c.begin(); - while( it_erase != c.end() ) - { - db.remove( *it_erase ); - it_erase = c.begin(); - } - BOOST_REQUIRE( c.size() == 0 ); - - cnt = 0; - BOOST_TEST_MESSAGE( "Creating `v.size()` objects. There are 'v.size() - 1' collisions." ); - for( const auto& item : v ) - { - auto constructor = [ &item ]( Object &obj ) - { - obj.set_id( item ); - obj.name = "all_objects_have_the_same_name"; - obj.val = 667; - }; - - if( cnt == 0 ) - db.create< Object >( constructor ); - else - BOOST_CHECK_THROW( db.create< Object >( constructor ), std::logic_error ); - - cnt++; - } - BOOST_REQUIRE( c.size() == 1 ); - - auto it_only_one = c.begin(); - BOOST_REQUIRE( size_t( it_only_one->get_id() ) == v[0] ); - BOOST_REQUIRE( it_only_one->name == "all_objects_have_the_same_name" ); - BOOST_REQUIRE( it_only_one->val == 667 ); - - BOOST_TEST_MESSAGE( "Erasing one objects." ); - - db.remove( *it_only_one ); - BOOST_REQUIRE( c.size() == 0 ); - - cnt = 0; - BOOST_TEST_MESSAGE( "Creating `v.size()` objects." ); - for( const auto& item : v ) - { - auto constructor = [ &item, &cnt ]( Object &obj ) - { - obj.set_id( item ); - obj.name = "object nr:" + std::to_string( cnt++ ); - obj.val = 5000; - }; - db.create< Object >( constructor ); - } - BOOST_REQUIRE( c.size() == v.size() ); - - BOOST_TEST_MESSAGE( "Finding some objects according to given index." ); - - const auto& ordered_idx = db.get_index< IndexType, SimpleIndex >(); - const auto& composite_ordered_idx = db.get_index< IndexType, ComplexIndex >(); - - auto found = ordered_idx.find( v.size() - 1 ); - BOOST_REQUIRE( found != ordered_idx.end() ); - BOOST_REQUIRE( size_t( found->get_id() ) == v.size() - 1 ); - - found = ordered_idx.find( 987654 ); - BOOST_REQUIRE( found == ordered_idx.end() ); - - found = ordered_idx.find( v[0] ); - BOOST_REQUIRE( found != ordered_idx.end() ); - - auto cfound = composite_ordered_idx.find( boost::make_tuple( "stupid_name", 5000 ) ); - BOOST_REQUIRE( cfound == composite_ordered_idx.end() ); - - cfound = composite_ordered_idx.find( boost::make_tuple( "object nr:" + std::to_string( 0 ), 5000 ) ); - auto copy_cfound = cfound; - auto cfound2 = composite_ordered_idx.find( boost::make_tuple( "object nr:" + std::to_string( 9 ), 5000 ) ); - BOOST_REQUIRE( cfound == composite_ordered_idx.begin() ); - - { - auto last = composite_ordered_idx.end(); - --last; - BOOST_REQUIRE( cfound2 == last ); - } - - cnt = 0; - for ( auto it = cfound; it != composite_ordered_idx.end(); ++it ) - cnt++; - BOOST_REQUIRE( cnt == v.size() ); - - BOOST_TEST_MESSAGE( "Removing all data using iterators from 'ComplexIndex' index"); - while( copy_cfound != composite_ordered_idx.end() ) - { - auto tmp = copy_cfound; - ++copy_cfound; - db.remove( *tmp ); - } - BOOST_REQUIRE( c.size() == 0 ); -} - -template < typename IndexType, typename Object, typename SimpleIndex, typename ComplexIndex, typename AnotherComplexIndex > -void misc_test3( const std::vector< uint64_t >& v, chainbase::database& db ) -{ - const auto& c = db.get_index< IndexType, SimpleIndex >(); - - BOOST_TEST_MESSAGE( "Creating `v.size()` objects" ); - for ( const auto& item : v ) - { - auto constructor = [ &item ]( Object &obj ) - { - obj.set_id( item ); - obj.val = item + 1; - obj.val2 = item + 2; - obj.val3 = item + 3; - }; - db.create< Object >( constructor ); - } - BOOST_REQUIRE( v.size() == c.size() ); - - BOOST_TEST_MESSAGE( "Finding some objects according to given index." ); - const auto& ordered_idx = db.get_index< IndexType, SimpleIndex >(); - const auto& composite_ordered_idx = db.get_index< IndexType, ComplexIndex >(); - const auto& another_composite_ordered_idx = db.get_index< IndexType, AnotherComplexIndex >(); - - auto found = ordered_idx.lower_bound( 5739854 ); - BOOST_REQUIRE( found == ordered_idx.end() ); - - found = ordered_idx.upper_bound( 5739854 ); - BOOST_REQUIRE( found == ordered_idx.end() ); - - found = ordered_idx.lower_bound( ordered_idx.begin()->get_id() ); - BOOST_REQUIRE( found == ordered_idx.begin() ); - - auto found2 = ordered_idx.upper_bound( ordered_idx.begin()->get_id() ); - BOOST_REQUIRE( found == --found2 ); - - auto cfound = composite_ordered_idx.find( boost::make_tuple( 667, 5000 ) ); - BOOST_REQUIRE( cfound == composite_ordered_idx.end() ); - - cfound = composite_ordered_idx.find( boost::make_tuple( 0 + 1, 0 + 2 ) ); - BOOST_REQUIRE( cfound != composite_ordered_idx.end() ); - - auto another_cfound = another_composite_ordered_idx.find( boost::make_tuple( 0 + 1, 0 + 3 ) ); - BOOST_REQUIRE( another_cfound != another_composite_ordered_idx.end() ); - - BOOST_TEST_MESSAGE( "Removing 1 object using iterator from 'AnotherComplexIndex' index"); - db.remove( *another_cfound ); - BOOST_REQUIRE( c.size() == v.size() - 1 ); - - another_cfound = another_composite_ordered_idx.find( boost::make_tuple( 0 + 1, 0 + 3 ) ); - BOOST_REQUIRE( another_cfound == another_composite_ordered_idx.end() ); -} diff --git a/libraries/net/node.cpp b/libraries/net/node.cpp index 88a452953a..0a2a4bd802 100644 --- a/libraries/net/node.cpp +++ b/libraries/net/node.cpp @@ -657,10 +657,10 @@ namespace graphene { namespace net { void close(); - void accept_connection_task(peer_connection_ptr new_peer); + void accept_connection_task(const peer_connection_ptr& new_peer); void accept_loop(); void send_hello_message(const peer_connection_ptr& peer); - void connect_to_task(peer_connection_ptr new_peer, const fc::ip::endpoint& remote_endpoint); + void connect_to_task(const peer_connection_ptr& new_peer, const fc::ip::endpoint& remote_endpoint); bool is_connection_to_endpoint_in_progress(const fc::ip::endpoint& remote_endpoint); void move_peer_to_active_list(const peer_connection_ptr& peer); @@ -1356,7 +1356,7 @@ namespace graphene { namespace net { uint32_t handshaking_timeout = _node_configuration.peer_inactivity_timeout; fc::time_point handshaking_disconnect_threshold = fc::time_point::now() - fc::seconds(handshaking_timeout); - for( const peer_connection_ptr handshaking_peer : _handshaking_connections ) + for( const peer_connection_ptr& handshaking_peer : _handshaking_connections ) if( handshaking_peer->connection_initiation_time < handshaking_disconnect_threshold && handshaking_peer->get_last_message_received_time() < handshaking_disconnect_threshold && handshaking_peer->get_last_message_sent_time() < handshaking_disconnect_threshold ) @@ -1687,7 +1687,7 @@ namespace graphene { namespace net { bool node_impl::is_accepting_new_connections() { VERIFY_CORRECT_THREAD(); - return !_node_is_shutting_down && !_p2p_network_connect_loop_done.canceled() && + return !_node_is_shutting_down && (!_p2p_network_connect_loop_done.valid() || !_p2p_network_connect_loop_done.canceled()) && get_number_of_connections() <= _node_configuration.maximum_number_of_connections; } @@ -1723,13 +1723,13 @@ namespace graphene { namespace net { dlog("is_already_connected_to_id returning true because the peer is us"); return true; } - for (const peer_connection_ptr active_peer : _active_connections) + for (const peer_connection_ptr& active_peer : _active_connections) if (node_id == active_peer->node_id) { dlog("is_already_connected_to_id returning true because the peer is already in our active list"); return true; } - for (const peer_connection_ptr handshaking_peer : _handshaking_connections) + for (const peer_connection_ptr& handshaking_peer : _handshaking_connections) if (node_id == handshaking_peer->node_id) { dlog("is_already_connected_to_id returning true because the peer is already in our handshaking list"); @@ -1893,7 +1893,7 @@ namespace graphene { namespace net { if (!_hard_fork_block_numbers.empty()) user_data["last_known_fork_block_number"] = _hard_fork_block_numbers.back(); - user_data["chain_id"] = _delegate->get_chain_id(); + user_data["chain_id"] = _delegate->get_new_chain_id(); return user_data; } @@ -2048,7 +2048,7 @@ namespace graphene { namespace net { "I'm already connected to you"); originating_peer->their_state = peer_connection::their_connection_state::connection_rejected; originating_peer->send_message(message(connection_rejected)); - dlog("Received a hello_message from peer ${peer} that I'm already connected to (with id ${id}), rejection", + wlog("Received a hello_message from peer ${peer} that I'm already connected to (with id ${id}), rejection", ("peer", originating_peer->get_remote_endpoint()) ("id", originating_peer->node_id)); } @@ -2062,7 +2062,7 @@ namespace graphene { namespace net { "you are not in my allowed_peers list"); originating_peer->their_state = peer_connection::their_connection_state::connection_rejected; originating_peer->send_message( message(connection_rejected ) ); - dlog( "Received a hello_message from peer ${peer} who isn't in my allowed_peers list, rejection", ("peer", originating_peer->get_remote_endpoint() ) ); + wlog( "Received a hello_message from peer ${peer} who isn't in my allowed_peers list, rejection", ("peer", originating_peer->get_remote_endpoint() ) ); } #endif // ENABLE_P2P_DEBUGGING_API else @@ -2107,7 +2107,7 @@ namespace graphene { namespace net { "not accepting any more incoming connections"); originating_peer->their_state = peer_connection::their_connection_state::connection_rejected; originating_peer->send_message(message(connection_rejected)); - dlog("Received a hello_message from peer ${peer}, but I'm not accepting any more connections, rejection", + wlog("Received a hello_message from peer ${peer}, but I'm not accepting any more connections, rejection", ("peer", originating_peer->get_remote_endpoint())); } else @@ -2300,7 +2300,9 @@ namespace graphene { namespace net { } catch (const peer_is_on_an_unreachable_fork&) { - dlog("Peer is on a fork and there's no set of blocks we can provide to switch them to our fork"); + //This was true when a full block synopsis was sent by the peer, now it's not + //dlog("Peer is on a fork and there's no set of blocks we can provide to switch them to our fork"); + dlog("We don't have any blocks that fit within the reversible synopsis sent by peer: either it is too far ahead of us (likely case) or it is on a fork that can't be recovered from"); // we reply with an empty list as if we had an empty blockchain; // we don't want to disconnect because they may be able to provide // us with blocks on their chain @@ -2879,6 +2881,7 @@ namespace graphene { namespace net { auto sync_item_iter = originating_peer->sync_items_requested_from_peer.find(requested_item.item_hash); if (sync_item_iter != originating_peer->sync_items_requested_from_peer.end()) { + _active_sync_requests.erase(*sync_item_iter); originating_peer->sync_items_requested_from_peer.erase(sync_item_iter); if (originating_peer->peer_needs_sync_items_from_us) @@ -2917,7 +2920,7 @@ namespace graphene { namespace net { bool we_advertised_this_item_to_a_peer = false; bool we_requested_this_item_from_a_peer = false; - for (const peer_connection_ptr peer : _active_connections) + for (const peer_connection_ptr& peer : _active_connections) { if (peer->inventory_advertised_to_peer.find(advertised_item_id) != peer->inventory_advertised_to_peer.end()) { @@ -3886,15 +3889,15 @@ namespace graphene { namespace net { size_t minutes_to_average = std::min(_average_network_write_speed_minutes.size(), (size_t)15); boost::circular_buffer::iterator start_iter = _average_network_write_speed_minutes.end() - minutes_to_average; - reply.upload_rate_fifteen_minutes = std::accumulate(start_iter, _average_network_write_speed_minutes.end(), 0) / (uint32_t)minutes_to_average; + reply.upload_rate_fifteen_minutes = std::accumulate(start_iter, _average_network_write_speed_minutes.end(), 0u) / (uint32_t)minutes_to_average; start_iter = _average_network_read_speed_minutes.end() - minutes_to_average; - reply.download_rate_fifteen_minutes = std::accumulate(start_iter, _average_network_read_speed_minutes.end(), 0) / (uint32_t)minutes_to_average; + reply.download_rate_fifteen_minutes = std::accumulate(start_iter, _average_network_read_speed_minutes.end(), 0u) / (uint32_t)minutes_to_average; minutes_to_average = std::min(_average_network_write_speed_minutes.size(), (size_t)60); start_iter = _average_network_write_speed_minutes.end() - minutes_to_average; - reply.upload_rate_one_hour = std::accumulate(start_iter, _average_network_write_speed_minutes.end(), 0) / (uint32_t)minutes_to_average; + reply.upload_rate_one_hour = std::accumulate(start_iter, _average_network_write_speed_minutes.end(), 0u) / (uint32_t)minutes_to_average; start_iter = _average_network_read_speed_minutes.end() - minutes_to_average; - reply.download_rate_one_hour = std::accumulate(start_iter, _average_network_read_speed_minutes.end(), 0) / (uint32_t)minutes_to_average; + reply.download_rate_one_hour = std::accumulate(start_iter, _average_network_read_speed_minutes.end(), 0u) / (uint32_t)minutes_to_average; } fc::time_point now = fc::time_point::now(); @@ -4341,7 +4344,7 @@ namespace graphene { namespace net { } } // node_impl::close() - void node_impl::accept_connection_task( peer_connection_ptr new_peer ) + void node_impl::accept_connection_task( const peer_connection_ptr& new_peer ) { new_peer->accept_connection(); // this blocks until the secure connection is fully negotiated send_hello_message(new_peer); @@ -4419,7 +4422,7 @@ namespace graphene { namespace net { peer->send_message(message(hello)); } - void node_impl::connect_to_task(peer_connection_ptr new_peer, + void node_impl::connect_to_task(const peer_connection_ptr& new_peer, const fc::ip::endpoint& remote_endpoint) { VERIFY_CORRECT_THREAD(); @@ -4671,6 +4674,7 @@ namespace graphene { namespace net { } std::string error_message = error_message_stream.str(); ulog(error_message); + wlog(error_message); _delegate->error_encountered( error_message, fc::oexception() ); fc::usleep( fc::seconds(GRAPHENE_NET_PORT_WAIT_DELAY_SECONDS) ); } diff --git a/libraries/plugins/account_by_key/account_by_key_plugin.cpp b/libraries/plugins/account_by_key/account_by_key_plugin.cpp index 9e6674888d..c812775145 100644 --- a/libraries/plugins/account_by_key/account_by_key_plugin.cpp +++ b/libraries/plugins/account_by_key/account_by_key_plugin.cpp @@ -56,22 +56,25 @@ struct pre_operation_visitor void operator()( const account_update_operation& op )const { _plugin.clear_cache(); - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.account ); - if( acct_itr ) _plugin.cache_auths( *acct_itr ); + cache_auths_of( op.account ); } void operator()( const account_update2_operation& op )const { _plugin.clear_cache(); - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.account ); - if( acct_itr ) _plugin.cache_auths( *acct_itr ); + cache_auths_of( op.account ); + } + + void operator()( const create_claimed_account_operation& op )const + { + _plugin.clear_cache(); + cache_auths_of( op.new_account_name ); } void operator()( const recover_account_operation& op )const { _plugin.clear_cache(); - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.account_to_recover ); - if( acct_itr ) _plugin.cache_auths( *acct_itr ); + cache_auths_of( op.account_to_recover ); } void operator()( const pow_operation& op )const @@ -83,6 +86,14 @@ struct pre_operation_visitor { _plugin.clear_cache(); } + +private: + + void cache_auths_of( const account_name_type& account_name )const + { + auto acct_itr = _plugin._db.find< account_authority_object, by_account >( account_name ); + if( acct_itr ) _plugin.cache_auths( *acct_itr ); + } }; struct pow2_work_get_account_visitor @@ -109,38 +120,37 @@ struct post_operation_visitor void operator()( const account_create_operation& op )const { - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.new_account_name ); - if( acct_itr ) _plugin.update_key_lookup( *acct_itr ); + update_key_lookup_of( op.new_account_name ); } void operator()( const account_create_with_delegation_operation& op )const { - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.new_account_name ); - if( acct_itr ) _plugin.update_key_lookup( *acct_itr ); + update_key_lookup_of( op.new_account_name ); } void operator()( const account_update_operation& op )const { - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.account ); - if( acct_itr ) _plugin.update_key_lookup( *acct_itr ); + update_key_lookup_of( op.account ); } void operator()( const account_update2_operation& op )const { - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.account ); - if( acct_itr ) _plugin.update_key_lookup( *acct_itr ); + update_key_lookup_of( op.account ); + } + + void operator()( const create_claimed_account_operation& op )const + { + update_key_lookup_of( op.new_account_name ); } void operator()( const recover_account_operation& op )const { - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.account_to_recover ); - if( acct_itr ) _plugin.update_key_lookup( *acct_itr ); + update_key_lookup_of( op.account_to_recover ); } void operator()( const pow_operation& op )const { - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( op.worker_account ); - if( acct_itr ) _plugin.update_key_lookup( *acct_itr ); + update_key_lookup_of( op.worker_account ); } void operator()( const pow2_operation& op )const @@ -148,8 +158,7 @@ struct post_operation_visitor const account_name_type* worker_account = op.work.visit( pow2_work_get_account_visitor() ); if( worker_account == nullptr ) return; - auto acct_itr = _plugin._db.find< account_authority_object, by_account >( *worker_account ); - if( acct_itr ) _plugin.update_key_lookup( *acct_itr ); + update_key_lookup_of( *worker_account ); } void operator()( const hardfork_operation& op )const @@ -172,6 +181,14 @@ struct post_operation_visitor } } } + +private: + + void update_key_lookup_of( const account_name_type& account_name )const + { + auto acct_itr = _plugin._db.find< account_authority_object, by_account >( account_name ); + if( acct_itr ) _plugin.update_key_lookup( *acct_itr ); + } }; void account_by_key_plugin_impl::clear_cache() diff --git a/libraries/plugins/account_history/account_history_plugin.cpp b/libraries/plugins/account_history/account_history_plugin.cpp index e864f1b897..274f116139 100644 --- a/libraries/plugins/account_history/account_history_plugin.cpp +++ b/libraries/plugins/account_history/account_history_plugin.cpp @@ -47,7 +47,7 @@ class account_history_plugin_impl struct operation_visitor { - operation_visitor( database& db, const operation_notification& note, const operation_object*& n, account_name_type i, bool prune ) + operation_visitor( database& db, const operation_notification& note, const operation_object*& n, const account_name_type& i, bool prune ) :_db(db), _note(note), new_obj(n), item(i), _prune(prune) {} typedef void result_type; @@ -123,7 +123,7 @@ struct operation_visitor struct operation_visitor_filter : operation_visitor { - operation_visitor_filter( database& db, const operation_notification& note, const operation_object*& n, account_name_type i, const flat_set< string >& filter, bool p, bool blacklist ): + operation_visitor_filter( database& db, const operation_notification& note, const operation_object*& n, const account_name_type& i, const flat_set< string >& filter, bool p, bool blacklist ): operation_visitor( db, note, n, i, p ), _filter( filter ), _blacklist( blacklist ) {} const flat_set< string >& _filter; diff --git a/libraries/plugins/account_history_rocksdb/CMakeLists.txt b/libraries/plugins/account_history_rocksdb/CMakeLists.txt index 95f112d15e..533ae0a0fc 100644 --- a/libraries/plugins/account_history_rocksdb/CMakeLists.txt +++ b/libraries/plugins/account_history_rocksdb/CMakeLists.txt @@ -6,12 +6,13 @@ add_library( account_history_rocksdb_plugin ) target_link_libraries( account_history_rocksdb_plugin - rocksdb chain_plugin hive_chain hive_protocol json_rpc_plugin rocksdb + rocksdb chain_plugin hive_chain hive_protocol json_rpc_plugin rocksdb condenser_api_plugin ) target_include_directories( account_history_rocksdb_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/../../vendor/rocksdb/include" + ) install( TARGETS diff --git a/libraries/plugins/account_history_rocksdb/account_history_rocksdb_plugin.cpp b/libraries/plugins/account_history_rocksdb/account_history_rocksdb_plugin.cpp index a3e9c858bf..22f7812f5f 100644 --- a/libraries/plugins/account_history_rocksdb/account_history_rocksdb_plugin.cpp +++ b/libraries/plugins/account_history_rocksdb/account_history_rocksdb_plugin.cpp @@ -17,6 +17,8 @@ #include #include +#include + #include #include @@ -77,6 +79,7 @@ using hive::protocol::signed_transaction; using hive::chain::operation_notification; using hive::chain::transaction_id_type; +using hive::plugins::condenser_api::legacy_asset; using hive::utilities::benchmark_dumper; using ::rocksdb::DB; @@ -486,6 +489,7 @@ class account_history_rocksdb_plugin::impl final impl( account_history_rocksdb_plugin& self, const bpo::variables_map& options, const bfs::path& storagePath) : _self(self), _mainDb(appbase::app().get_plugin().db()), + _blockchainStoragePath(appbase::app().get_plugin().state_storage_dir()), _storagePath(storagePath), _writeBuffer(_storage, _columnHandles) { @@ -571,6 +575,9 @@ class account_history_rocksdb_plugin::impl final void openDb() { + //Very rare case - when a synchronization starts from the scratch and a node has AH plugin with rocksdb enabled and directories don't exist yet + bfs::create_directories( _blockchainStoragePath ); + createDbSchema(_storagePath); auto columnDefs = prepareColumnDefinitions(true); @@ -598,7 +605,8 @@ class account_history_rocksdb_plugin::impl final // opening the db, so that is not a good place to write the initial lib. try { - get_lib(); + uint32_t lib = get_lib(); + _cached_irreversible_block.store(lib); } catch( fc::assert_exception& ) { @@ -812,9 +820,8 @@ class account_history_rocksdb_plugin::impl final if(_storage == nullptr) return; - /// If there are still not yet saved changes let's do it now. - if(_collectedOps != 0) - flushWriteBuffer(); + // lib (last irreversible block) has not been saved so far + flushWriteBuffer(); ::rocksdb::FlushOptions fOptions; for(const auto& cf : _columnHandles) @@ -863,6 +870,7 @@ class account_history_rocksdb_plugin::impl final account_history_rocksdb_plugin& _self; chain::database& _mainDb; + bfs::path _blockchainStoragePath; bfs::path _storagePath; std::unique_ptr _storage; std::vector _columnHandles; @@ -930,6 +938,28 @@ class account_history_rocksdb_plugin::impl final bool _reindexing = false; bool _prune = false; + + struct saved_balances + { + asset hive_balance = asset(0, HIVE_SYMBOL); + asset savings_hive_balance = asset(0, HIVE_SYMBOL); + + asset hbd_balance = asset(0, HBD_SYMBOL); + asset savings_hbd_balance = asset(0, HBD_SYMBOL); + + asset vesting_shares = asset(0, VESTS_SYMBOL); + asset delegated_vesting_shares = asset(0, VESTS_SYMBOL); + asset received_vesting_shares = asset(0, VESTS_SYMBOL); + + asset reward_hbd_balance = asset(0, HBD_SYMBOL); + asset reward_hive_balance = asset(0, HIVE_SYMBOL); + asset reward_vesting_balance = asset(0, VESTS_SYMBOL); + asset reward_vesting_hive_balance = asset(0, HIVE_SYMBOL); + }; + optional _balance_csv_filename; + std::ofstream _balance_csv_file; + std::map _saved_balances; + unsigned _balance_csv_line_count = 0; }; void account_history_rocksdb_plugin::impl::collectOptions(const boost::program_options::variables_map& options) @@ -969,6 +999,27 @@ void account_history_rocksdb_plugin::impl::collectOptions(const boost::program_o if(_blacklisted_op_list.empty() == false) ilog( "Account History: blacklisting ops ${o}", ("o", _blacklisted_op_list) ); + if (options.count("account-history-rocksdb-dump-balance-history")) + { + _balance_csv_filename = options.at("account-history-rocksdb-dump-balance-history").as(); + _balance_csv_file.open(*_balance_csv_filename, std::ios_base::out | std::ios_base::trunc); + _balance_csv_file << "account" << "," + << "block_num" << "," + << "timestamp" << "," + << "hive" << "," + << "savings_hive" << "," + << "hbd" << "," + << "savings_hbd" << "," + << "vesting_shares" << "," + << "delegated_vesting_shares" << "," + << "received_vesting_shares" << "," + << "reward_hive" << "," + << "reward_hbd" << "," + << "reward_vesting_shares" << "," + << "reward_vesting_hive" << "\n"; + _balance_csv_file.flush(); + } + appbase::app().get_plugin< chain::chain_plugin >().report_state_options( _self.name(), state_opts ); } @@ -1149,7 +1200,7 @@ account_history_rocksdb_plugin::impl::collectReversibleOps(uint32_t* blockRangeB *blockRangeEnd = retVal.back().block + 1; } - return std::move(retVal); + return retVal; } void account_history_rocksdb_plugin::impl::find_account_history_data(const account_name_type& name, uint64_t start, @@ -1313,8 +1364,10 @@ std::pair< uint32_t, uint64_t > account_history_rocksdb_plugin::impl::enumVirtua break; } - if(processor(op, op.id, false)) - ++cntLimit; + /// Accept only virtual operations + if(op.virtual_op > 0) + if(processor(op, op.id, false)) + ++cntLimit; lastFoundBlock = op.block; } @@ -1393,8 +1446,10 @@ std::pair< uint32_t, uint64_t > account_history_rocksdb_plugin::impl::enumVirtua break; } - if(processor(op, op.id, false)) - ++cntLimit; + /// Accept only virtual operations + if(op.virtual_op > 0) + if(processor(op, op.id, false)) + ++cntLimit; lastFoundBlock = op.block; } @@ -1542,7 +1597,11 @@ uint32_t account_history_rocksdb_plugin::impl::get_lib(const uint32_t* fallbackI uint32_t lib = 0; load( lib, data.data(), data.size() ); - return lib; + + if(lib < _cached_irreversible_block) + return _cached_irreversible_block; + else + return lib; } void account_history_rocksdb_plugin::impl::update_lib( uint32_t lib ) @@ -1798,11 +1857,20 @@ void account_history_rocksdb_plugin::impl::on_post_reindex(const hive::chain::re flushStorage(); _collectedOpsWriteLimit = 1; _reindexing = false; - update_lib( note.last_block_number ); // We always reindex irreversible blocks. + uint32_t last_irreversible_block_num =_mainDb.get_last_irreversible_block_num(); + update_lib( last_irreversible_block_num ); // Set same value as in main database, as result of witness participation printReport( note.last_block_number, "RocksDB data reindex finished." ); } +std::string get_asset_amount(const asset& amount) +{ + std::string asset_with_amount_string = legacy_asset::from_asset(amount).to_string(); + size_t space_pos = asset_with_amount_string.find(' '); + assert(space_pos != std::string::npos); + return asset_with_amount_string.substr(0, space_pos); +} + void account_history_rocksdb_plugin::impl::printReport(uint32_t blockNo, const char* detailText) const { ilog("${t}Processed blocks: ${n}, containing: ${tx} transactions and ${op} operations.\n" @@ -1882,8 +1950,7 @@ void account_history_rocksdb_plugin::impl::importData(unsigned int blockLimit) } ); - if(_collectedOps != 0) - flushWriteBuffer(); + flushWriteBuffer(); const auto& measure = dumper.measure(blockNo, [](benchmark_dumper::index_memory_details_cntr_t&, bool){}); ilog( "RocksDb data import - Performance report at block ${n}. Elapsed time: ${rt} ms (real), ${ct} ms (cpu). Memory usage: ${cm} (current), ${pm} (peak) kilobytes.", @@ -1966,31 +2033,66 @@ void account_history_rocksdb_plugin::impl::on_irreversible_block( uint32_t block /// In case of genesis block (and fresh testnet) there can be no LIB at begin. - if( block_num <= get_lib(&fallbackIrreversibleBlock) ) return; + auto stored_lib = get_lib(&fallbackIrreversibleBlock); + + FC_ASSERT(block_num > stored_lib, "New irreversible block: ${nb} can't be less than already stored one: ${ob}", ("nb", block_num)("ob", stored_lib)); - _currently_persisted_irreversible_block.store(block_num); + auto& volatileOpsGenericIndex = _mainDb.get_mutable_index(); const auto& volatile_idx = _mainDb.get_index< volatile_operation_index, by_block >(); auto itr = volatile_idx.begin(); - vector< const volatile_operation_object* > to_delete; + _currently_persisted_irreversible_block.store(block_num); { std::lock_guard lk(_currently_persisted_irreversible_mtx); - while(itr != volatile_idx.end() && itr->block <= block_num) + // it is unlikely we get here but flush storage in this case + if(itr != volatile_idx.end() && itr->block < block_num) { - rocksdb_operation_object obj(*itr); - importOperation(obj, itr->impacted); - to_delete.push_back(&(*itr)); - ++itr; - } + flushStorage(); - for(const volatile_operation_object* o : to_delete) - { - _mainDb.remove(*o); + while(itr != volatile_idx.end() && itr->block < block_num) + { + auto comp = []( const rocksdb_operation_object& lhs, const rocksdb_operation_object& rhs ) + { + return std::tie( lhs.block, lhs.trx_in_block, lhs.op_in_trx, lhs.trx_id, lhs.virtual_op ) < std::tie( rhs.block, rhs.trx_in_block, rhs.op_in_trx, rhs.trx_id, rhs.virtual_op ); + }; + std::set< rocksdb_operation_object, decltype(comp) > ops( comp ); + find_operations_by_block(itr->block, false, // don't include reversible, only already imported ops + [&ops](const rocksdb_operation_object& op) + { + ops.emplace(op); + } + ); + + uint32_t this_itr_block = itr->block; + while(itr != volatile_idx.end() && itr->block == this_itr_block) + { + rocksdb_operation_object obj(*itr); + // check that operation is already stored as irreversible as it will be not imported + FC_ASSERT(ops.count(obj), "operation in block ${block} was not imported until irreversible block ${irreversible}", ("block", this_itr_block)("irreversible", block_num)); + hive::protocol::operation op = fc::raw::unpack_from_buffer< hive::protocol::operation >( obj.serialized_op ); + wlog("prevented importing duplicate operation from block ${block} when handling irreversible block ${irreversible}, operation: ${op}", + ("block", obj.block)("irreversible", block_num)("op", fc::json::to_string(op))); + itr++; + } + } } + /// Range of reversible (volatile) ops to be processed should come from blocks (stored_lib, block_num] + auto moveRangeBeginI = volatile_idx.upper_bound(stored_lib); + + FC_ASSERT(moveRangeBeginI == volatile_idx.begin() || moveRangeBeginI == volatile_idx.end(), "All volatile ops processed by previous irreversible blocks should be already flushed"); + + auto moveRangeEndI = volatile_idx.upper_bound(block_num); + volatileOpsGenericIndex.move_to_external_storage(moveRangeBeginI, moveRangeEndI, [this](const volatile_operation_object& operation) -> void + { + rocksdb_operation_object obj(operation); + importOperation(obj, operation.impacted); + } + ); + update_lib(block_num); } @@ -2009,6 +2111,113 @@ void account_history_rocksdb_plugin::impl::on_pre_apply_block(const block_notifi void account_history_rocksdb_plugin::impl::on_post_apply_block(const block_notification& bn) { + if (_balance_csv_filename) + { + // check the balances of all tracked accounts, emit a line in the CSV file if any have changed + // since the last block + const auto& account_idx = _mainDb.get_index().indices().get(); + for (auto range : _tracked_accounts) + { + const std::string& lower = range.first; + const std::string& upper = range.second; + + auto account_iter = account_idx.lower_bound(range.first); + while (account_iter != account_idx.end() && + account_iter->name <= upper) + { + const account_object& account = *account_iter; + + auto saved_balance_iter = _saved_balances.find(account_iter->name); + bool balances_changed = saved_balance_iter == _saved_balances.end(); + saved_balances& saved_balance_record = _saved_balances[account_iter->name]; + + if (saved_balance_record.hive_balance != account.balance) + { + saved_balance_record.hive_balance = account.balance; + balances_changed = true; + } + if (saved_balance_record.savings_hive_balance != account.savings_balance) + { + saved_balance_record.savings_hive_balance = account.savings_balance; + balances_changed = true; + } + if (saved_balance_record.hbd_balance != account.hbd_balance) + { + saved_balance_record.hbd_balance = account.hbd_balance; + balances_changed = true; + } + if (saved_balance_record.savings_hbd_balance != account.savings_hbd_balance) + { + saved_balance_record.savings_hbd_balance = account.savings_hbd_balance; + balances_changed = true; + } + + if (saved_balance_record.reward_hbd_balance != account.reward_hbd_balance) + { + saved_balance_record.reward_hbd_balance = account.reward_hbd_balance; + balances_changed = true; + } + if (saved_balance_record.reward_hive_balance != account.reward_hive_balance) + { + saved_balance_record.reward_hive_balance = account.reward_hive_balance; + balances_changed = true; + } + if (saved_balance_record.reward_vesting_balance != account.reward_vesting_balance) + { + saved_balance_record.reward_vesting_balance = account.reward_vesting_balance; + balances_changed = true; + } + if (saved_balance_record.reward_vesting_hive_balance != account.reward_vesting_hive) + { + saved_balance_record.reward_vesting_hive_balance = account.reward_vesting_hive; + balances_changed = true; + } + + if (saved_balance_record.vesting_shares != account.vesting_shares) + { + saved_balance_record.vesting_shares = account.vesting_shares; + balances_changed = true; + } + if (saved_balance_record.delegated_vesting_shares != account.delegated_vesting_shares) + { + saved_balance_record.delegated_vesting_shares = account.delegated_vesting_shares; + balances_changed = true; + } + if (saved_balance_record.received_vesting_shares != account.received_vesting_shares) + { + saved_balance_record.received_vesting_shares = account.received_vesting_shares; + balances_changed = true; + } + + if (balances_changed) + { + _balance_csv_file << (string)account.name << "," + << bn.block.block_num() << "," + << bn.block.timestamp.to_iso_string() << "," + << get_asset_amount(saved_balance_record.hive_balance) << "," + << get_asset_amount(saved_balance_record.savings_hive_balance) << "," + << get_asset_amount(saved_balance_record.hbd_balance) << "," + << get_asset_amount(saved_balance_record.savings_hbd_balance) << "," + << get_asset_amount(saved_balance_record.vesting_shares) << "," + << get_asset_amount(saved_balance_record.delegated_vesting_shares) << "," + << get_asset_amount(saved_balance_record.received_vesting_shares) << "," + << get_asset_amount(saved_balance_record.reward_hive_balance) << "," + << get_asset_amount(saved_balance_record.reward_hbd_balance) << "," + << get_asset_amount(saved_balance_record.reward_vesting_balance) << "," + << get_asset_amount(saved_balance_record.reward_vesting_hive_balance) << "\n"; + // flush every 10k lines + ++_balance_csv_line_count; + if (_balance_csv_line_count > 10000) + { + _balance_csv_line_count = 0; + _balance_csv_file.flush(); + } + } + ++account_iter; + } + } + } + if(_reindexing) return; _currently_processed_block.store(0); @@ -2047,6 +2256,7 @@ void account_history_rocksdb_plugin::set_program_options( "Allows to force immediate data import at plugin startup. By default storage is supplied during reindex process.") ("account-history-rocksdb-stop-import-at-block", bpo::value()->default_value(0), "Allows to specify block number, the data import process should stop at.") + ("account-history-rocksdb-dump-balance-history", boost::program_options::value< string >(), "Dumps balances for all tracked accounts to a CSV file every time they change") ; } diff --git a/libraries/plugins/apis/account_history_api/account_history_api.cpp b/libraries/plugins/apis/account_history_api/account_history_api.cpp index 3cf403b6a3..0b0e4a709a 100644 --- a/libraries/plugins/apis/account_history_api/account_history_api.cpp +++ b/libraries/plugins/apis/account_history_api/account_history_api.cpp @@ -263,7 +263,7 @@ struct filtering_visitor { typedef void result_type; - bool check(uint32_t filter, const hive::protocol::operation& op) + bool check(uint64_t filter, const hive::protocol::operation& op) { _filter = filter; _accepted = false; @@ -283,10 +283,13 @@ struct filtering_visitor (clear_null_account_balance_operation)(proposal_pay_operation)(sps_fund_operation) (hardfork_hive_operation)(hardfork_hive_restore_operation)(delayed_voting_operation) (consolidate_treasury_balance_operation)(effective_comment_vote_operation)(ineffective_delete_comment_operation) - (sps_convert_operation) ) + (sps_convert_operation)(expired_account_notification_operation)(changed_recovery_account_operation) + (transfer_to_vesting_completed_operation)(pow_reward_operation)(vesting_shares_split_operation) + (account_created_operation)(fill_collateralized_convert_request_operation)(system_warning_operation) + (fill_recurrent_transfer_operation)(failed_recurrent_transfer_operation) ) private: - uint32_t _filter = 0; + uint64_t _filter = 0; bool _accepted = false; }; diff --git a/libraries/plugins/apis/account_history_api/include/hive/plugins/account_history_api/account_history_api.hpp b/libraries/plugins/apis/account_history_api/include/hive/plugins/account_history_api/account_history_api.hpp index 609edc4bb2..6903714c60 100644 --- a/libraries/plugins/apis/account_history_api/include/hive/plugins/account_history_api/account_history_api.hpp +++ b/libraries/plugins/apis/account_history_api/include/hive/plugins/account_history_api/account_history_api.hpp @@ -30,13 +30,13 @@ struct api_operation_object } hive::protocol::transaction_id_type trx_id; - uint32_t block = 0; - uint32_t trx_in_block = 0; - uint32_t op_in_trx = 0; - uint32_t virtual_op = 0; - uint64_t operation_id = 0; - fc::time_point_sec timestamp; - hive::protocol::operation op; + uint32_t block = 0; + uint32_t trx_in_block = 0; + uint32_t op_in_trx = 0; + uint32_t virtual_op = 0; + uint64_t operation_id = 0; + fc::time_point_sec timestamp; + hive::protocol::operation op; bool operator<( const api_operation_object& obj ) const { @@ -91,33 +91,43 @@ struct get_account_history_return std::map< uint32_t, api_operation_object > history; }; -enum enum_vops_filter : uint32_t +enum enum_vops_filter : uint64_t { - fill_convert_request_operation = 0x000001, - author_reward_operation = 0x000002, - curation_reward_operation = 0x000004, - comment_reward_operation = 0x000008, - liquidity_reward_operation = 0x000010, - interest_operation = 0x000020, - fill_vesting_withdraw_operation = 0x000040, - fill_order_operation = 0x000080, - shutdown_witness_operation = 0x000100, - fill_transfer_from_savings_operation = 0x000200, - hardfork_operation = 0x000400, - comment_payout_update_operation = 0x000800, - return_vesting_delegation_operation = 0x001000, - comment_benefactor_reward_operation = 0x002000, - producer_reward_operation = 0x004000, - clear_null_account_balance_operation = 0x008000, - proposal_pay_operation = 0x010000, - sps_fund_operation = 0x020000, - hardfork_hive_operation = 0x040000, - hardfork_hive_restore_operation = 0x080000, - delayed_voting_operation = 0x100000, - consolidate_treasury_balance_operation = 0x200000, - effective_comment_vote_operation = 0x400000, - ineffective_delete_comment_operation = 0x800000, - sps_convert_operation = 0x1000000 + fill_convert_request_operation = 0x0'00000001ull, + author_reward_operation = 0x0'00000002ull, + curation_reward_operation = 0x0'00000004ull, + comment_reward_operation = 0x0'00000008ull, + liquidity_reward_operation = 0x0'00000010ull, + interest_operation = 0x0'00000020ull, + fill_vesting_withdraw_operation = 0x0'00000040ull, + fill_order_operation = 0x0'00000080ull, + shutdown_witness_operation = 0x0'00000100ull, + fill_transfer_from_savings_operation = 0x0'00000200ull, + hardfork_operation = 0x0'00000400ull, + comment_payout_update_operation = 0x0'00000800ull, + return_vesting_delegation_operation = 0x0'00001000ull, + comment_benefactor_reward_operation = 0x0'00002000ull, + producer_reward_operation = 0x0'00004000ull, + clear_null_account_balance_operation = 0x0'00008000ull, + proposal_pay_operation = 0x0'00010000ull, + sps_fund_operation = 0x0'00020000ull, + hardfork_hive_operation = 0x0'00040000ull, + hardfork_hive_restore_operation = 0x0'00080000ull, + delayed_voting_operation = 0x0'00100000ull, + consolidate_treasury_balance_operation = 0x0'00200000ull, + effective_comment_vote_operation = 0x0'00400000ull, + ineffective_delete_comment_operation = 0x0'00800000ull, + sps_convert_operation = 0x0'01000000ull, + expired_account_notification_operation = 0x0'02000000ull, + changed_recovery_account_operation = 0x0'04000000ull, + transfer_to_vesting_completed_operation = 0x0'08000000ull, + pow_reward_operation = 0x0'10000000ull, + vesting_shares_split_operation = 0x0'20000000ull, + account_created_operation = 0x0'40000000ull, + fill_collateralized_convert_request_operation = 0x0'80000000ull, + system_warning_operation = 0x1'00000000ull, + fill_recurrent_transfer_operation = 0x2'00000000ull, + failed_recurrent_transfer_operation = 0x4'00000000ull, }; /** Allows to specify range of blocks to retrieve virtual operations for. @@ -125,7 +135,7 @@ enum enum_vops_filter : uint32_t * \param block_range_end - last block number (exclusive) to search for virtual operations * \param operation_begin - starting virtual operation in given block (inclusive) * \param limit - a limit of retrieved operations - * \param block_range_end - a filter that decides which an operation matches - used bitwise filtering equals to position in `hive::protocol::operation` + * \param filter - a filter that decides which an operation matches - used bitwise filtering equals to position in `hive::protocol::operation` */ struct enum_virtual_ops_args { @@ -137,7 +147,7 @@ struct enum_virtual_ops_args fc::optional group_by_block; fc::optional< uint64_t > operation_begin; fc::optional< uint32_t > limit; - fc::optional< uint32_t > filter; + fc::optional< uint64_t > filter; }; struct ops_array_wrapper @@ -158,7 +168,7 @@ struct ops_array_wrapper struct enum_virtual_ops_return { vector ops; - std::set ops_by_block; + std::set ops_by_block; uint32_t next_block_range_begin = 0; uint64_t next_operation_begin = 0; }; diff --git a/libraries/plugins/apis/condenser_api/condenser_api.cpp b/libraries/plugins/apis/condenser_api/condenser_api.cpp index 48169b9b36..8e0a166307 100644 --- a/libraries/plugins/apis/condenser_api/condenser_api.cpp +++ b/libraries/plugins/apis/condenser_api/condenser_api.cpp @@ -28,6 +28,8 @@ #define CHECK_ARG_SIZE( s ) \ FC_ASSERT( args.size() == s, "Expected #s argument(s), was ${n}", ("n", args.size()) ); +#define ASSET_TO_REAL( asset ) (double)( asset.amount.value ) + namespace hive { namespace plugins { namespace condenser_api { namespace detail @@ -80,6 +82,7 @@ namespace detail (get_expiring_vesting_delegations) (get_witnesses) (get_conversion_requests) + (get_collateralized_conversion_requests) (get_witness_by_account) (get_witnesses_by_vote) (lookup_witness_accounts) @@ -132,9 +135,11 @@ namespace detail (get_recent_trades) (get_market_history) (get_market_history_buckets) + (is_known_transaction) (list_proposals) (find_proposals) (list_proposal_votes) + (find_recurrent_transfers) ) void on_post_apply_block( const signed_block& b ); @@ -173,7 +178,7 @@ namespace detail auto tags = _tags_api->get_trending_tags( { args[0].as< string >(), args[1].as< uint32_t >() } ).tags; vector< api_tag_object > result; - + result.reserve( tags.size() ); for( const auto& t : tags ) { result.push_back( api_tag_object( t ) ); @@ -595,6 +600,24 @@ namespace detail return result; } + DEFINE_API_IMPL( condenser_api_impl, get_collateralized_conversion_requests ) + { + CHECK_ARG_SIZE( 1 ) + auto requests = _database_api->find_collateralized_conversion_requests( + { + args[0].as< account_name_type >() + }).requests; + + get_collateralized_conversion_requests_return result; + + for( auto& r : requests ) + { + result.push_back( api_collateralized_convert_request_object( r ) ); + } + + return result; + } + DEFINE_API_IMPL( condenser_api_impl, get_witness_by_account ) { CHECK_ARG_SIZE( 1 ) @@ -681,11 +704,10 @@ namespace detail { result.push_back( *itr ); - // if( itr->sell_price.base.symbol == HIVE_SYMBOL ) - // result.back().real_price = (~result.back().sell_price).to_real(); - // else - // result.back().real_price = (result.back().sell_price).to_real(); - result.back().real_price = 0.0; + if( itr->sell_price.base.symbol == HIVE_SYMBOL ) + result.back().real_price = ASSET_TO_REAL( itr->sell_price.quote ) / ASSET_TO_REAL( itr->sell_price.base ); + else + result.back().real_price = ASSET_TO_REAL( itr->sell_price.base ) / ASSET_TO_REAL( itr->sell_price.quote ); ++itr; } @@ -796,244 +818,77 @@ namespace detail DEFINE_API_IMPL( condenser_api_impl, get_post_discussions_by_payout ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_post_discussions_by_payout( - args[0].as< tags::get_post_discussions_by_payout_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_comment_discussions_by_payout ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_comment_discussions_by_payout( - args[0].as< tags::get_comment_discussions_by_payout_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_trending ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_trending( - args[0].as< tags::get_discussions_by_trending_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_created ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_created( - args[0].as< tags::get_discussions_by_created_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_active ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_active( - args[0].as< tags::get_discussions_by_active_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_cashout ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_cashout( - args[0].as< tags::get_discussions_by_cashout_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_votes ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_votes( - args[0].as< tags::get_discussions_by_votes_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_children ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_children( - args[0].as< tags::get_discussions_by_children_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_hot ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_hot( - args[0].as< tags::get_discussions_by_hot_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_feed ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_feed( - args[0].as< tags::get_discussions_by_feed_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_blog ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_blog( - args[0].as< tags::get_discussions_by_blog_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_comments ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_comments( - args[0].as< tags::get_discussions_by_comments_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_promoted ) { - CHECK_ARG_SIZE( 1 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_promoted( - args[0].as< tags::get_discussions_by_promoted_args >() ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_replies_by_last_update ) { - FC_ASSERT( false, "Supported by hivemind" ); + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_discussions_by_author_before_date ) { - CHECK_ARG_SIZE( 4 ) - FC_ASSERT( _tags_api, "tags_api_plugin not enabled." ); - - auto discussions = _tags_api->get_discussions_by_author_before_date( { args[0].as< account_name_type >(), args[1].as< string >(), args[2].as< time_point_sec >(), args[3].as< uint32_t >() } ).discussions; - vector< discussion > result; - - for( auto& d : discussions ) - { - result.push_back( discussion( d ) ); - } - - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_account_history ) @@ -1176,14 +1031,7 @@ namespace detail DEFINE_API_IMPL( condenser_api_impl, get_feed ) { - FC_ASSERT( args.size() == 2 || args.size() == 3, "Expected 2-3 arguments, was ${n}", ("n", args.size()) ); - FC_ASSERT( _follow_api, "follow_api_plugin not enabled." ); - - auto feed = _follow_api->get_feed( { args[0].as< account_name_type >(), args[1].as< uint32_t >(), args.size() == 3 ? args[2].as< uint32_t >() : 500 } ).feed; - get_feed_return result; - result.resize( feed.size() ); - result.insert( result.end(), feed.begin(), feed.end() ); - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_blog_entries ) @@ -1196,14 +1044,7 @@ namespace detail DEFINE_API_IMPL( condenser_api_impl, get_blog ) { - FC_ASSERT( args.size() == 2 || args.size() == 3, "Expected 2-3 arguments, was ${n}", ("n", args.size()) ); - FC_ASSERT( _follow_api, "follow_api_plugin not enabled." ); - - auto blog = _follow_api->get_blog( { args[0].as< account_name_type >(), args[1].as< uint32_t >(), args.size() == 3 ? args[2].as< uint32_t >() : 500 } ).blog; - get_blog_return result; - result.resize( blog.size() ); - result.insert( result.end(), blog.begin(), blog.end() ); - return result; + FC_ASSERT( false, "Supported by hivemind" ); } DEFINE_API_IMPL( condenser_api_impl, get_account_reputations ) @@ -1303,6 +1144,13 @@ namespace detail return _market_history_api->get_market_history_buckets( {} ).bucket_sizes; } + DEFINE_API_IMPL( condenser_api_impl, is_known_transaction ) + { + CHECK_ARG_SIZE( 1 ) + + return _database_api->is_known_transaction( { args[0].as() } ).is_known; + } + DEFINE_API_IMPL( condenser_api_impl, list_proposals ) { FC_ASSERT( args.size() >= 3 && args.size() <= 6, "Expected 3-6 argument, was ${n}", ("n", args.size()) ); @@ -1354,6 +1202,14 @@ namespace detail return _database_api->list_proposal_votes( list_args ).proposal_votes; } + + DEFINE_API_IMPL( condenser_api_impl, find_recurrent_transfers ) + { + CHECK_ARG_SIZE( 1 ) + + return _database_api->find_recurrent_transfers( { args[0].as< account_name_type >() } ).recurrent_transfers; + } + void condenser_api_impl::on_post_apply_block( const signed_block& b ) { try { boost::lock_guard< boost::mutex > guard( _mtx ); @@ -1546,6 +1402,7 @@ DEFINE_READ_APIS( condenser_api, (get_expiring_vesting_delegations) (get_witnesses) (get_conversion_requests) + (get_collateralized_conversion_requests) (get_witness_by_account) (get_witnesses_by_vote) (lookup_witness_accounts) @@ -1592,9 +1449,11 @@ DEFINE_READ_APIS( condenser_api, (get_trade_history) (get_recent_trades) (get_market_history) + (is_known_transaction) (list_proposals) (list_proposal_votes) (find_proposals) + (find_recurrent_transfers) ) } } } // hive::plugins::condenser_api diff --git a/libraries/plugins/apis/condenser_api/condenser_api_legacy_asset.cpp b/libraries/plugins/apis/condenser_api/condenser_api_legacy_asset.cpp index 55f6cf49c9..4814f66272 100644 --- a/libraries/plugins/apis/condenser_api/condenser_api_legacy_asset.cpp +++ b/libraries/plugins/apis/condenser_api/condenser_api_legacy_asset.cpp @@ -146,8 +146,8 @@ legacy_asset legacy_asset::from_string( const string& from ) try { string s = fc::trim( from ); - auto space_pos = s.find( " " ); - auto dot_pos = s.find( "." ); + auto space_pos = s.find( ' ' ); + auto dot_pos = s.find( '.' ); FC_ASSERT( space_pos != std::string::npos ); diff --git a/libraries/plugins/apis/condenser_api/condenser_api_legacy_operations.cpp b/libraries/plugins/apis/condenser_api/condenser_api_legacy_operations.cpp index 82a896ff90..91165c1cb8 100644 --- a/libraries/plugins/apis/condenser_api/condenser_api_legacy_operations.cpp +++ b/libraries/plugins/apis/condenser_api/condenser_api_legacy_operations.cpp @@ -121,4 +121,17 @@ void from_variant( const fc::variant& v, hive::plugins::condenser_api::legacy_po old_sv_from_variant( v, sv ); } +void to_variant( const hive::plugins::condenser_api::legacy_update_proposal_extensions& sv, fc::variant& v ) +{ + old_sv_to_variant( sv, v ); +} + +void from_variant( const fc::variant& v, hive::plugins::condenser_api::legacy_update_proposal_extensions& sv ) +{ + if( v.is_array() ) + old_sv_from_variant( v, sv ); + else + new_sv_from_variant( v, sv ); +} + } // fc diff --git a/libraries/plugins/apis/condenser_api/include/hive/plugins/condenser_api/condenser_api.hpp b/libraries/plugins/apis/condenser_api/include/hive/plugins/condenser_api/condenser_api.hpp index 9c6b5b7020..586fa25b7d 100644 --- a/libraries/plugins/apis/condenser_api/include/hive/plugins/condenser_api/condenser_api.hpp +++ b/libraries/plugins/apis/condenser_api/include/hive/plugins/condenser_api/condenser_api.hpp @@ -150,7 +150,9 @@ struct api_account_object last_root_post( a.last_root_post ), last_vote_time( a.last_vote_time ), post_bandwidth( a.post_bandwidth ), - pending_claimed_accounts( a.pending_claimed_accounts ) + pending_claimed_accounts( a.pending_claimed_accounts ), + open_recurrent_transfers( a.open_recurrent_transfers ), + governance_vote_expiration_ts( a.governance_vote_expiration_ts ) { voting_power = _compute_voting_power(a); proxied_vsf_votes.insert( proxied_vsf_votes.end(), a.proxied_vsf_votes.begin(), a.proxied_vsf_votes.end() ); @@ -237,7 +239,11 @@ struct api_account_object share_type pending_claimed_accounts = 0; + uint16_t open_recurrent_transfers = 0; + fc::optional< vector< delayed_votes_data > > delayed_votes; + + time_point_sec governance_vote_expiration_ts; }; struct extended_account : public api_account_object @@ -265,99 +271,6 @@ struct extended_account : public api_account_object optional< vector< string > > recommended; /// posts recommened for this user }; -struct api_comment_object -{ - api_comment_object( const database_api::api_comment_object& c ): - id( c.id ), - category( c.category ), - parent_author( c.parent_author ), - parent_permlink( c.parent_permlink ), - author( c.author ), - permlink( c.permlink ), - title( c.title ), - body( c.body ), - json_metadata( c.json_metadata ), - last_update( c.last_update ), - created( c.created ), - active( c.active ), - last_payout( c.last_payout ), - depth( c.depth ), - children( c.children ), - net_rshares( c.net_rshares ), - abs_rshares( c.abs_rshares ), - vote_rshares( c.vote_rshares ), - children_abs_rshares( c.children_abs_rshares ), - cashout_time( c.cashout_time ), - max_cashout_time( c.max_cashout_time ), - total_vote_weight( c.total_vote_weight ), - reward_weight( c.reward_weight ), - total_payout_value( legacy_asset::from_asset( c.total_payout_value ) ), - curator_payout_value( legacy_asset::from_asset( c.curator_payout_value ) ), - author_rewards( c.author_rewards ), - net_votes( c.net_votes ), - root_author( c.root_author ), - root_permlink( c.root_permlink ), - max_accepted_payout( legacy_asset::from_asset( c.max_accepted_payout ) ), - percent_hbd( c.percent_hbd ), - allow_replies( c.allow_replies ), - allow_votes( c.allow_votes ), - allow_curation_rewards( c.allow_curation_rewards ) - { - for( auto& route : c.beneficiaries ) - { - beneficiaries.push_back( route ); - } - } - - api_comment_object(){} - - comment_id_type id; - string category; - string parent_author; - string parent_permlink; - string author; - string permlink; - - string title; - string body; - string json_metadata; - time_point_sec last_update; - time_point_sec created; - time_point_sec active; - time_point_sec last_payout; - - uint8_t depth = 0; - uint32_t children = 0; - - share_type net_rshares; - share_type abs_rshares; - share_type vote_rshares; - - share_type children_abs_rshares; - time_point_sec cashout_time; - time_point_sec max_cashout_time; - uint64_t total_vote_weight = 0; - - uint16_t reward_weight = 0; - - legacy_asset total_payout_value = legacy_asset::from_asset( asset( 0, HBD_SYMBOL ) ); - legacy_asset curator_payout_value = legacy_asset::from_asset( asset( 0, HBD_SYMBOL ) ); - - share_type author_rewards; - - int32_t net_votes = 0; - - account_name_type root_author; - string root_permlink; - - legacy_asset max_accepted_payout = legacy_asset::from_asset( asset( 0, HBD_SYMBOL ) ); - uint16_t percent_hbd = 0; - bool allow_replies = false; - bool allow_votes = false; - bool allow_curation_rewards = false; - vector< beneficiary_route_type > beneficiaries; -}; - struct extended_dynamic_global_properties { extended_dynamic_global_properties() {} @@ -399,7 +312,14 @@ struct extended_dynamic_global_properties vesting_reward_percent( o.vesting_reward_percent ), sps_fund_percent( o.sps_fund_percent ), sps_interval_ledger( legacy_asset::from_asset( o.sps_interval_ledger ) ), - downvote_pool_percent( o.downvote_pool_percent ) + downvote_pool_percent( o.downvote_pool_percent ), + current_remove_threshold( o.current_remove_threshold ), + early_voting_seconds( o.early_voting_seconds ), + mid_voting_seconds( o.mid_voting_seconds ), + max_consecutive_recurrent_transfer_failures( o.max_consecutive_recurrent_transfer_failures ), + max_recurrent_transfer_end_date( o.max_recurrent_transfer_end_date ), + min_recurrent_transfers_recurrence( o.min_recurrent_transfers_recurrence ), + max_open_recurrent_transfers( o.max_open_recurrent_transfers ) {} uint32_t head_block_number = 0; @@ -455,6 +375,16 @@ struct extended_dynamic_global_properties legacy_asset sps_interval_ledger = legacy_asset::from_asset( asset( 0, HBD_SYMBOL ) ); uint16_t downvote_pool_percent = 0; + + int16_t current_remove_threshold = HIVE_GLOBAL_REMOVE_THRESHOLD; + + uint64_t early_voting_seconds = 0; + uint64_t mid_voting_seconds = 0; + + uint8_t max_consecutive_recurrent_transfer_failures = HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES; + uint16_t max_recurrent_transfer_end_date = HIVE_MAX_RECURRENT_TRANSFER_END_DATE; + uint8_t min_recurrent_transfers_recurrence = HIVE_MIN_RECURRENT_TRANSFERS_RECURRENCE; + uint16_t max_open_recurrent_transfers = HIVE_MAX_OPEN_RECURRENT_TRANSFERS; }; struct api_witness_object @@ -557,7 +487,10 @@ struct api_feed_history_object { api_feed_history_object() {} api_feed_history_object( const database_api::api_feed_history_object& f ) : - current_median_history( f.current_median_history ) + current_median_history( f.current_median_history ), + market_median_history( f.market_median_history ), + current_min_history( f.current_min_history ), + current_max_history( f.current_max_history ) { for( auto& p : f.price_history ) { @@ -567,6 +500,9 @@ struct api_feed_history_object feed_history_id_type id; legacy_price current_median_history; + legacy_price market_median_history; + legacy_price current_min_history; + legacy_price current_max_history; deque< legacy_price > price_history; }; @@ -709,6 +645,27 @@ struct api_convert_request_object time_point_sec conversion_date; }; +struct api_collateralized_convert_request_object +{ + api_collateralized_convert_request_object() {} + api_collateralized_convert_request_object( const database_api::api_collateralized_convert_request_object& c ) : + id( c.id ), + owner( c.owner ), + requestid( c.requestid ), + collateral_amount( legacy_asset::from_asset( c.collateral_amount ) ), + converted_amount( legacy_asset::from_asset( c.converted_amount) ), + conversion_date( c.conversion_date ) + {} + + collateralized_convert_request_id_type id; + + account_name_type owner; + uint32_t requestid = 0; + legacy_asset collateral_amount; + legacy_asset converted_amount; + time_point_sec conversion_date; +}; + struct api_proposal_object { api_proposal_object() {} @@ -737,42 +694,6 @@ struct api_proposal_object uint64_t total_votes = 0; }; -struct discussion : public api_comment_object -{ - discussion() {} - - discussion( const api_comment_object& c ) : api_comment_object( c ) {} - - discussion( const tags::discussion& d ) : - api_comment_object( d ), - url( d.url ), - root_title( d.root_title ), - pending_payout_value( legacy_asset::from_asset( d.pending_payout_value ) ), - total_pending_payout_value( legacy_asset::from_asset( d.total_pending_payout_value ) ), - active_votes( d.active_votes ), - replies( d.replies ), - author_reputation( d.author_reputation ), - promoted( legacy_asset::from_asset( d.promoted ) ), - body_length( d.body_length ), - reblogged_by( d.reblogged_by ), - first_reblogged_by( d.first_reblogged_by ), - first_reblogged_on( d.first_reblogged_on ) - {} - - string url; /// /category/@rootauthor/root_permlink#author/permlink - string root_title; - legacy_asset pending_payout_value = legacy_asset::from_asset( asset( 0, HBD_SYMBOL ) ); ///< HBD - legacy_asset total_pending_payout_value = legacy_asset::from_asset( asset( 0, HBD_SYMBOL ) ); ///< HBD including replies - vector< tags::vote_state > active_votes; - vector< string > replies; ///< author/slug mapping - share_type author_reputation = 0; - legacy_asset promoted = legacy_asset::from_asset( asset( 0, HBD_SYMBOL ) ); - uint32_t body_length = 0; - vector< account_name_type > reblogged_by; - optional< account_name_type > first_reblogged_by; - optional< time_point_sec > first_reblogged_on; -}; - struct tag_index { vector< tags::tag_name_type > trending; /// pending payouts @@ -807,16 +728,12 @@ struct state tag_index tag_idx; /** - * "" is the global tags::discussion index + * "" is the global tags::discussion_ index */ map< string, discussion_index > discussion_idx; - map< string, api_tag_object > tags; + map< string, api_tag_object > tags; - /** - * map from account/slug to full nested tags::discussion - */ - map< string, discussion > content; map< string, extended_account > accounts; map< string, api_witness_object > witnesses; @@ -867,46 +784,6 @@ struct broadcast_transaction_synchronous_return bool expired = false; }; -struct comment_feed_entry -{ - comment_feed_entry( const follow::comment_feed_entry& c ) : - comment( c.comment ), - reblog_on( c.reblog_on ), - entry_id( c.entry_id ) - { - reblog_by.resize( c.reblog_by.size() ); - - for( auto& a : c.reblog_by ) - { - reblog_by.push_back( a ); - } - } - - comment_feed_entry() {} - - api_comment_object comment; - vector< account_name_type > reblog_by; - time_point_sec reblog_on; - uint32_t entry_id = 0; -}; - -struct comment_blog_entry -{ - comment_blog_entry( const follow::comment_blog_entry& c ) : - comment( c.comment ), - blog( c.blog ), - reblog_on( c.reblog_on ), - entry_id( c.entry_id ) - {} - - comment_blog_entry() {} - - api_comment_object comment; - string blog; - time_point_sec reblog_on; - uint32_t entry_id = 0; -}; - struct ticker { ticker() {} @@ -984,6 +861,12 @@ struct market_trade legacy_asset open_pays; }; +struct no_return {}; +using discussion_api_object = no_return; +using discussion_api_object_collection = no_return; +using comment_feed_entry = no_return; +using comment_blog_entry = no_return; + #define DEFINE_API_ARGS( api_name, arg_type, return_type ) \ typedef arg_type api_name ## _args; \ typedef return_type api_name ## _return; @@ -1020,6 +903,7 @@ DEFINE_API_ARGS( get_vesting_delegations, vector< variant >, ve DEFINE_API_ARGS( get_expiring_vesting_delegations, vector< variant >, vector< api_vesting_delegation_expiration_object > ) DEFINE_API_ARGS( get_witnesses, vector< variant >, vector< optional< api_witness_object > > ) DEFINE_API_ARGS( get_conversion_requests, vector< variant >, vector< api_convert_request_object > ) +DEFINE_API_ARGS( get_collateralized_conversion_requests, vector< variant >, vector< api_collateralized_convert_request_object > ) DEFINE_API_ARGS( get_witness_by_account, vector< variant >, optional< api_witness_object > ) DEFINE_API_ARGS( get_witnesses_by_vote, vector< variant >, vector< api_witness_object > ) DEFINE_API_ARGS( lookup_witness_accounts, vector< variant >, vector< account_name_type > ) @@ -1033,24 +917,24 @@ DEFINE_API_ARGS( verify_authority, vector< variant >, bo DEFINE_API_ARGS( verify_account_authority, vector< variant >, bool ) DEFINE_API_ARGS( get_active_votes, vector< variant >, vector< tags::vote_state > ) DEFINE_API_ARGS( get_account_votes, vector< variant >, vector< account_vote > ) -DEFINE_API_ARGS( get_content, vector< variant >, discussion ) -DEFINE_API_ARGS( get_content_replies, vector< variant >, vector< discussion > ) +DEFINE_API_ARGS( get_content, vector< variant >, discussion_api_object ) +DEFINE_API_ARGS( get_content_replies, vector< variant >, discussion_api_object_collection ) DEFINE_API_ARGS( get_tags_used_by_author, vector< variant >, vector< tags::tag_count_object > ) -DEFINE_API_ARGS( get_post_discussions_by_payout, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_comment_discussions_by_payout, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_trending, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_created, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_active, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_cashout, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_votes, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_children, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_hot, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_feed, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_blog, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_comments, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_promoted, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_replies_by_last_update, vector< variant >, vector< discussion > ) -DEFINE_API_ARGS( get_discussions_by_author_before_date, vector< variant >, vector< discussion > ) +DEFINE_API_ARGS( get_post_discussions_by_payout, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_comment_discussions_by_payout, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_trending, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_created, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_active, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_cashout, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_votes, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_children, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_hot, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_feed, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_blog, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_comments, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_promoted, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_replies_by_last_update, vector< variant >, discussion_api_object_collection ) +DEFINE_API_ARGS( get_discussions_by_author_before_date, vector< variant >, discussion_api_object_collection ) DEFINE_API_ARGS( get_account_history, vector< variant >, get_account_history_return_type ) DEFINE_API_ARGS( broadcast_transaction, vector< variant >, json_rpc::void_type ) DEFINE_API_ARGS( broadcast_block, vector< variant >, json_rpc::void_type ) @@ -1071,9 +955,11 @@ DEFINE_API_ARGS( get_trade_history, vector< variant >, ve DEFINE_API_ARGS( get_recent_trades, vector< variant >, vector< market_trade > ) DEFINE_API_ARGS( get_market_history, vector< variant >, vector< market_history::bucket_object > ) DEFINE_API_ARGS( get_market_history_buckets, vector< variant >, flat_set< uint32_t > ) +DEFINE_API_ARGS( is_known_transaction, vector< variant >, bool ) DEFINE_API_ARGS( list_proposals, vector< variant >, vector< api_proposal_object > ) DEFINE_API_ARGS( find_proposals, vector< variant >, vector< api_proposal_object > ) DEFINE_API_ARGS( list_proposal_votes, vector< variant >, vector< database_api::api_proposal_vote_object > ) +DEFINE_API_ARGS( find_recurrent_transfers, vector< variant >, vector< database_api::api_recurrent_transfer_object > ) #undef DEFINE_API_ARGS @@ -1116,6 +1002,7 @@ class condenser_api (get_expiring_vesting_delegations) (get_witnesses) (get_conversion_requests) + (get_collateralized_conversion_requests) (get_witness_by_account) (get_witnesses_by_vote) (lookup_witness_accounts) @@ -1168,9 +1055,11 @@ class condenser_api (get_recent_trades) (get_market_history) (get_market_history_buckets) + (is_known_transaction) (list_proposals) (find_proposals) (list_proposal_votes) + (find_recurrent_transfers) ) private: @@ -1189,7 +1078,7 @@ FC_REFLECT( hive::plugins::condenser_api::api_tag_object, (name)(total_payouts)(net_votes)(top_posts)(comments)(trending) ) FC_REFLECT( hive::plugins::condenser_api::state, - (current_route)(props)(tag_idx)(tags)(content)(accounts)(witnesses)(discussion_idx)(witness_schedule)(feed_price)(error) ) + (current_route)(props)(tag_idx)(tags)(accounts)(witnesses)(discussion_idx)(witness_schedule)(feed_price)(error) ) FC_REFLECT( hive::plugins::condenser_api::api_limit_order_object, (id)(created)(expiration)(seller)(orderid)(for_sale)(sell_price)(real_price)(rewarded) ) @@ -1214,25 +1103,13 @@ FC_REFLECT( hive::plugins::condenser_api::api_account_object, (proxied_vsf_votes)(witnesses_voted_for) (last_post)(last_root_post)(last_vote_time) (post_bandwidth)(pending_claimed_accounts) - (delayed_votes) + (governance_vote_expiration_ts) + (delayed_votes)(open_recurrent_transfers) ) FC_REFLECT_DERIVED( hive::plugins::condenser_api::extended_account, (hive::plugins::condenser_api::api_account_object), (vesting_balance)(reputation)(transfer_history)(market_history)(post_history)(vote_history)(other_history)(witness_votes)(tags_usage)(guest_bloggers)(open_orders)(comments)(feed)(blog)(recent_replies)(recommended) ) -FC_REFLECT( hive::plugins::condenser_api::api_comment_object, - (id)(author)(permlink) - (category)(parent_author)(parent_permlink) - (title)(body)(json_metadata)(last_update)(created)(active)(last_payout) - (depth)(children) - (net_rshares)(abs_rshares)(vote_rshares) - (children_abs_rshares)(cashout_time)(max_cashout_time) - (total_vote_weight)(reward_weight)(total_payout_value)(curator_payout_value)(author_rewards)(net_votes) - (root_author)(root_permlink) - (max_accepted_payout)(percent_hbd)(allow_replies)(allow_votes)(allow_curation_rewards) - (beneficiaries) - ) - FC_REFLECT( hive::plugins::condenser_api::extended_dynamic_global_properties, (head_block_number)(head_block_id)(time) (current_witness)(total_pow)(num_pow_witnesses) @@ -1242,7 +1119,10 @@ FC_REFLECT( hive::plugins::condenser_api::extended_dynamic_global_properties, (hbd_interest_rate)(hbd_print_rate) (maximum_block_size)(required_actions_partition_percent)(current_aslot)(recent_slots_filled)(participation_count)(last_irreversible_block_num) (vote_power_reserve_rate)(delegation_return_period)(reverse_auction_seconds)(available_account_subsidies)(hbd_stop_percent)(hbd_start_percent) - (next_maintenance_time)(last_budget_time)(next_daily_maintenance_time)(content_reward_percent)(vesting_reward_percent)(sps_fund_percent)(sps_interval_ledger)(downvote_pool_percent) + (next_maintenance_time)(last_budget_time)(next_daily_maintenance_time)(content_reward_percent)(vesting_reward_percent)(sps_fund_percent)(sps_interval_ledger) + (downvote_pool_percent)(current_remove_threshold)(early_voting_seconds)(mid_voting_seconds) + (max_consecutive_recurrent_transfer_failures)(max_recurrent_transfer_end_date)(min_recurrent_transfers_recurrence) + (max_open_recurrent_transfers) ) FC_REFLECT( hive::plugins::condenser_api::api_witness_object, @@ -1283,6 +1163,9 @@ FC_REFLECT( hive::plugins::condenser_api::api_witness_schedule_object, FC_REFLECT( hive::plugins::condenser_api::api_feed_history_object, (id) (current_median_history) + (market_median_history) + (current_min_history) + (current_max_history) (price_history) ) @@ -1324,15 +1207,12 @@ FC_REFLECT( hive::plugins::condenser_api::api_vesting_delegation_expiration_obje FC_REFLECT( hive::plugins::condenser_api::api_convert_request_object, (id)(owner)(requestid)(amount)(conversion_date) ) +FC_REFLECT( hive::plugins::condenser_api::api_collateralized_convert_request_object, + (id)(owner)(requestid)(collateral_amount)(converted_amount)(conversion_date) ) + FC_REFLECT( hive::plugins::condenser_api::api_proposal_object, (id)(proposal_id)(creator)(receiver)(start_date)(end_date)(daily_pay)(subject)(permlink)(total_votes) ) -FC_REFLECT_DERIVED( hive::plugins::condenser_api::discussion, (hive::plugins::condenser_api::api_comment_object), - (url)(root_title)(pending_payout_value)(total_pending_payout_value) - (active_votes)(replies)(author_reputation)(promoted) - (body_length)(reblogged_by)(first_reblogged_by)(first_reblogged_on) - ) - FC_REFLECT( hive::plugins::condenser_api::scheduled_hardfork, (hf_version)(live_time) ) @@ -1346,12 +1226,6 @@ FC_REFLECT_ENUM( hive::plugins::condenser_api::withdraw_route_type, (incoming)(o FC_REFLECT( hive::plugins::condenser_api::broadcast_transaction_synchronous_return, (id)(block_num)(trx_num)(expired) ) -FC_REFLECT( hive::plugins::condenser_api::comment_feed_entry, - (comment)(reblog_by)(reblog_on)(entry_id) ) - -FC_REFLECT( hive::plugins::condenser_api::comment_blog_entry, - (comment)(blog)(reblog_on)(entry_id) ) - FC_REFLECT( hive::plugins::condenser_api::ticker, (latest)(lowest_ask)(highest_bid)(percent_change)(hive_volume)(hbd_volume) ) @@ -1366,3 +1240,5 @@ FC_REFLECT( hive::plugins::condenser_api::order_book, FC_REFLECT( hive::plugins::condenser_api::market_trade, (date)(current_pays)(open_pays) ) + +FC_REFLECT_EMPTY( hive::plugins::condenser_api::no_return ) diff --git a/libraries/plugins/apis/condenser_api/include/hive/plugins/condenser_api/condenser_api_legacy_operations.hpp b/libraries/plugins/apis/condenser_api/include/hive/plugins/condenser_api/condenser_api_legacy_operations.hpp index 24e996a608..415b8ecfe3 100644 --- a/libraries/plugins/apis/condenser_api/include/hive/plugins/condenser_api/condenser_api_legacy_operations.hpp +++ b/libraries/plugins/apis/condenser_api/include/hive/plugins/condenser_api/condenser_api_legacy_operations.hpp @@ -36,6 +36,9 @@ namespace hive { namespace plugins { namespace condenser_api { typedef vector< legacy_comment_options_extensions > legacy_comment_options_extensions_type; + typedef static_variant legacy_update_proposal_extensions; + typedef vector< legacy_update_proposal_extensions > legacy_update_proposal_extensions_type; + typedef static_variant< protocol::pow2, protocol::equihash_pow @@ -77,6 +80,12 @@ namespace hive { namespace plugins { namespace condenser_api { typedef consolidate_treasury_balance_operation legacy_consolidate_treasury_balance_operation; typedef delayed_voting_operation legacy_delayed_voting_operation; typedef sps_convert_operation legacy_sps_convert_operation; + typedef expired_account_notification_operation legacy_expired_account_notification_operation; + typedef changed_recovery_account_operation legacy_changed_recovery_account_operation; + typedef system_warning_operation legacy_system_warning_operation; + typedef ineffective_delete_comment_operation legacy_ineffective_delete_comment_operation; + typedef fill_recurrent_transfer_operation legacy_fill_recurrent_transfer_operation; + typedef failed_recurrent_transfer_operation legacy_failed_recurrent_transfer_operation; struct legacy_price { @@ -157,6 +166,33 @@ namespace hive { namespace plugins { namespace condenser_api { string json_metadata; }; + struct legacy_account_created_operation + { + legacy_account_created_operation() {} + legacy_account_created_operation( const account_created_operation& op ) : + new_account_name( op.new_account_name ), + creator( op.creator ), + initial_vesting_shares(legacy_asset::from_asset( op.initial_vesting_shares )), + initial_delegation(legacy_asset::from_asset( op.initial_delegation )) + {} + + operator account_created_operation()const + { + account_created_operation op; + op.new_account_name = new_account_name; + op.creator = creator; + op.initial_vesting_shares = initial_vesting_shares; + op.initial_delegation = initial_delegation; + return op; + } + + account_name_type new_account_name; + account_name_type creator; + legacy_asset initial_vesting_shares; + legacy_asset initial_delegation; + }; + + struct legacy_account_create_with_delegation_operation { legacy_account_create_with_delegation_operation() {} @@ -408,6 +444,32 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_asset amount; }; + struct legacy_transfer_to_vesting_completed_operation + { + legacy_transfer_to_vesting_completed_operation() {} + legacy_transfer_to_vesting_completed_operation( const transfer_to_vesting_completed_operation& op ) : + from_account(op.from_account), + to_account(op.to_account), + hive_vested(legacy_asset::from_asset( op.hive_vested )), + vesting_shares_received(legacy_asset::from_asset( op.vesting_shares_received )) + {} + + operator transfer_to_vesting_completed_operation()const + { + transfer_to_vesting_completed_operation op; + op.from_account = from_account; + op.to_account = to_account; + op.hive_vested = hive_vested; + op.vesting_shares_received = vesting_shares_received; + return op; + } + + account_name_type from_account; + account_name_type to_account; + legacy_asset hive_vested; + legacy_asset vesting_shares_received; + }; + struct legacy_create_proposal_operation { legacy_create_proposal_operation() {} @@ -452,7 +514,14 @@ namespace hive { namespace plugins { namespace condenser_api { daily_pay( legacy_asset::from_asset( op.daily_pay ) ), subject( op.subject ), permlink( op.permlink) - {} + { + for( const auto& e : op.extensions ) + { + legacy_update_proposal_extensions ext; + e.visit( convert_to_legacy_static_variant< legacy_update_proposal_extensions >( ext ) ); + extensions.push_back( e ); + } + } operator update_proposal_operation()const { @@ -462,6 +531,7 @@ namespace hive { namespace plugins { namespace condenser_api { op.daily_pay = daily_pay; op.subject = subject; op.permlink = permlink; + op.extensions.insert( extensions.begin(), extensions.end() ); return op; } @@ -470,6 +540,8 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_asset daily_pay; string subject; string permlink; + time_point_sec end_date; + legacy_update_proposal_extensions_type extensions; }; struct legacy_withdraw_vesting_operation @@ -569,6 +641,29 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_asset amount; }; + struct legacy_collateralized_convert_operation + { + legacy_collateralized_convert_operation() {} + legacy_collateralized_convert_operation( const collateralized_convert_operation& op ) : + owner( op.owner ), + requestid( op.requestid ), + amount( legacy_asset::from_asset( op.amount ) ) + {} + + operator collateralized_convert_operation()const + { + collateralized_convert_operation op; + op.owner = owner; + op.requestid = requestid; + op.amount = amount; + return op; + } + + account_name_type owner; + uint32_t requestid = 0; + legacy_asset amount; + }; + struct legacy_limit_order_create_operation { legacy_limit_order_create_operation() {} @@ -739,13 +834,14 @@ namespace hive { namespace plugins { namespace condenser_api { struct legacy_author_reward_operation { - legacy_author_reward_operation() {} - legacy_author_reward_operation( const author_reward_operation& op ) : + legacy_author_reward_operation() = default; + explicit legacy_author_reward_operation( const author_reward_operation& op ) : author( op.author ), permlink( op.permlink ), hbd_payout( legacy_asset::from_asset( op.hbd_payout ) ), hive_payout( legacy_asset::from_asset( op.hive_payout ) ), - vesting_payout( legacy_asset::from_asset( op.vesting_payout ) ) + vesting_payout( legacy_asset::from_asset( op.vesting_payout ) ), + payout_must_be_claimed( op.payout_must_be_claimed) {} operator author_reward_operation()const @@ -756,6 +852,7 @@ namespace hive { namespace plugins { namespace condenser_api { op.hbd_payout = hbd_payout; op.hive_payout = hive_payout; op.vesting_payout = vesting_payout; + op.payout_must_be_claimed = payout_must_be_claimed; return op; } @@ -764,16 +861,18 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_asset hbd_payout; legacy_asset hive_payout; legacy_asset vesting_payout; + bool payout_must_be_claimed = false; }; struct legacy_curation_reward_operation { - legacy_curation_reward_operation() {} - legacy_curation_reward_operation( const curation_reward_operation& op ) : + legacy_curation_reward_operation() = default; + explicit legacy_curation_reward_operation( const curation_reward_operation& op ) : curator( op.curator ), reward( legacy_asset::from_asset( op.reward ) ), comment_author( op.comment_author ), - comment_permlink( op.comment_permlink ) + comment_permlink( op.comment_permlink ), + payout_must_be_claimed( op.payout_must_be_claimed) {} operator curation_reward_operation()const @@ -783,6 +882,7 @@ namespace hive { namespace plugins { namespace condenser_api { op.reward = reward; op.comment_author = comment_author; op.comment_permlink = comment_permlink; + op.payout_must_be_claimed = payout_must_be_claimed; return op; } @@ -790,6 +890,7 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_asset reward; account_name_type comment_author; string comment_permlink; + bool payout_must_be_claimed = false; }; struct legacy_comment_reward_operation @@ -881,6 +982,35 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_asset amount_out; }; + struct legacy_fill_collateralized_convert_request_operation + { + legacy_fill_collateralized_convert_request_operation() {} + legacy_fill_collateralized_convert_request_operation( const fill_collateralized_convert_request_operation& op ) : + owner( op.owner ), + requestid( op.requestid ), + amount_in( legacy_asset::from_asset( op.amount_in ) ), + amount_out( legacy_asset::from_asset( op.amount_out ) ), + excess_collateral( legacy_asset::from_asset( op.excess_collateral ) ) + {} + + operator fill_collateralized_convert_request_operation()const + { + fill_collateralized_convert_request_operation op; + op.owner = owner; + op.requestid = requestid; + op.amount_in = amount_in; + op.amount_out = amount_out; + op.excess_collateral = excess_collateral; + return op; + } + + account_name_type owner; + uint32_t requestid; + legacy_asset amount_in; + legacy_asset amount_out; + legacy_asset excess_collateral; + }; + struct legacy_fill_vesting_withdraw_operation { legacy_fill_vesting_withdraw_operation() {} @@ -1064,10 +1194,54 @@ namespace hive { namespace plugins { namespace condenser_api { extensions_type extensions; }; + struct legacy_vesting_shares_split_operation + { + legacy_vesting_shares_split_operation() {} + legacy_vesting_shares_split_operation( const vesting_shares_split_operation& op ) : + owner( op.owner ), + vesting_shares_before_split( legacy_asset::from_asset( op.vesting_shares_before_split ) ), + vesting_shares_after_split( legacy_asset::from_asset( op.vesting_shares_after_split ) ) + {} + + operator vesting_shares_split_operation()const + { + vesting_shares_split_operation op; + op.owner = owner; + op.vesting_shares_before_split = vesting_shares_before_split; + op.vesting_shares_after_split = vesting_shares_after_split; + return op; + } + + account_name_type owner; + legacy_asset vesting_shares_before_split; + legacy_asset vesting_shares_after_split; + }; + + struct legacy_pow_reward_operation + { + legacy_pow_reward_operation() {} + legacy_pow_reward_operation( const pow_reward_operation& op ) : + worker( op.worker ), + reward( legacy_asset::from_asset( op.reward ) ) + {} + + operator pow_reward_operation()const + { + pow_reward_operation op; + op.worker = worker; + op.reward = reward; + return op; + } + + account_name_type worker; + legacy_asset reward; + }; + struct legacy_proposal_pay_operation { legacy_proposal_pay_operation() {} legacy_proposal_pay_operation( const proposal_pay_operation& op ) : + proposal_id( op.proposal_id ), receiver( op.receiver ), payer( op.payer ), payment( legacy_asset::from_asset( op.payment ) ), @@ -1078,6 +1252,7 @@ namespace hive { namespace plugins { namespace condenser_api { operator proposal_pay_operation()const { proposal_pay_operation op; + op.proposal_id = proposal_id; op.receiver = receiver; op.payer = payer; op.payment = payment; @@ -1086,6 +1261,7 @@ namespace hive { namespace plugins { namespace condenser_api { return op; } + uint32_t proposal_id = 0; account_name_type receiver; account_name_type payer; legacy_asset payment; @@ -1168,6 +1344,69 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_asset hive_transferred; }; + struct legacy_effective_comment_vote_operation{ + legacy_effective_comment_vote_operation() {}; + legacy_effective_comment_vote_operation(const effective_comment_vote_operation& op) : + voter( op.voter ), author( op.author ), + permlink( op.permlink ), weight( op.weight ), + rshares( op.rshares ), total_vote_weight( op.total_vote_weight ), + pending_payout( legacy_asset::from_asset( op.pending_payout ) ) + {} + + operator effective_comment_vote_operation()const + { + effective_comment_vote_operation op; + op.voter = voter; + op.author = author; + op.permlink = permlink; + op.weight = weight; + op.rshares = rshares; + op.total_vote_weight = total_vote_weight; + op.pending_payout = pending_payout; + return op; + } + + account_name_type voter; + account_name_type author; + string permlink; + uint64_t weight = 0; + int64_t rshares = 0; + uint64_t total_vote_weight = 0; + legacy_asset pending_payout; + }; + + struct legacy_recurrent_transfer_operation + { + legacy_recurrent_transfer_operation() {} + legacy_recurrent_transfer_operation( const recurrent_transfer_operation& op ) : + from( op.from ), + to( op.to ), + amount( legacy_asset::from_asset( op.amount ) ), + memo( op.memo ), + recurrence( op.recurrence ), + executions( op.executions ) + {} + + operator recurrent_transfer_operation()const + { + recurrent_transfer_operation op; + op.from = from; + op.to = to; + op.amount = amount; + op.memo = memo; + op.recurrence = recurrence; + op.executions = executions; + return op; + } + + account_name_type from; + account_name_type to; + legacy_asset amount; + string memo; + uint16_t recurrence = 0; + uint16_t executions = 0; + }; + typedef fc::static_variant< legacy_vote_operation, legacy_comment_operation, @@ -1217,6 +1456,7 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_update_proposal_votes_operation, legacy_remove_proposal_operation, legacy_update_proposal_operation, + legacy_collateralized_convert_operation, legacy_fill_convert_request_operation, legacy_author_reward_operation, legacy_curation_reward_operation, @@ -1239,7 +1479,20 @@ namespace hive { namespace plugins { namespace condenser_api { legacy_hardfork_hive_restore_operation, legacy_delayed_voting_operation, legacy_consolidate_treasury_balance_operation, - legacy_sps_convert_operation + legacy_sps_convert_operation, + legacy_expired_account_notification_operation, + legacy_changed_recovery_account_operation, + legacy_transfer_to_vesting_completed_operation, + legacy_account_created_operation, //for unknown reason this and two below are in different order than in hive::protocol::operation + legacy_vesting_shares_split_operation, //since the order is affecting external libraries + legacy_pow_reward_operation, //I'm not sure it can be changed to proper one + legacy_fill_collateralized_convert_request_operation, + legacy_system_warning_operation, + legacy_effective_comment_vote_operation, + legacy_ineffective_delete_comment_operation, + legacy_recurrent_transfer_operation, + legacy_fill_recurrent_transfer_operation, + legacy_failed_recurrent_transfer_operation > legacy_operation; struct legacy_operation_conversion_visitor @@ -1284,6 +1537,11 @@ namespace hive { namespace plugins { namespace condenser_api { bool operator()( const consolidate_treasury_balance_operation& op )const { l_op = op; return true; } bool operator()( const delayed_voting_operation& op )const { l_op = op; return true; } bool operator()( const sps_convert_operation& op )const { l_op = op; return true; } + bool operator()( const expired_account_notification_operation& op )const { l_op = op; return true; } + bool operator()( const changed_recovery_account_operation& op )const { l_op = op; return true; } + bool operator()( const ineffective_delete_comment_operation& op )const { l_op = op; return true; } + bool operator()( const fill_recurrent_transfer_operation& op )const { l_op = op; return true; } + bool operator()( const failed_recurrent_transfer_operation& op )const { l_op = op; return true; } bool operator()( const transfer_operation& op )const { @@ -1297,6 +1555,12 @@ namespace hive { namespace plugins { namespace condenser_api { return true; } + bool operator()( const transfer_to_vesting_completed_operation& op )const + { + l_op = legacy_transfer_to_vesting_completed_operation( op ); + return true; + } + bool operator()( const withdraw_vesting_operation& op )const { l_op = legacy_withdraw_vesting_operation( op ); @@ -1321,12 +1585,24 @@ namespace hive { namespace plugins { namespace condenser_api { return true; } + bool operator()( const collateralized_convert_operation& op )const + { + l_op = legacy_collateralized_convert_operation( op ); + return true; + } + bool operator()( const account_create_operation& op )const { l_op = legacy_account_create_operation( op ); return true; } + bool operator()( const account_created_operation& op )const + { + l_op = legacy_account_created_operation( op ); + return true; + } + bool operator()( const witness_update_operation& op )const { l_op = legacy_witness_update_operation( op ); @@ -1399,6 +1675,12 @@ namespace hive { namespace plugins { namespace condenser_api { return true; } + bool operator()( const fill_collateralized_convert_request_operation& op )const + { + l_op = legacy_fill_collateralized_convert_request_operation( op ); + return true; + } + bool operator()( const author_reward_operation& op )const { l_op = legacy_author_reward_operation( op ); @@ -1471,6 +1753,18 @@ namespace hive { namespace plugins { namespace condenser_api { return true; } + bool operator()( const vesting_shares_split_operation& op )const + { + l_op = legacy_vesting_shares_split_operation( op ); + return true; + } + + bool operator()( const pow_reward_operation& op )const + { + l_op = legacy_pow_reward_operation( op ); + return true; + } + bool operator()( const create_proposal_operation& op )const { l_op = legacy_create_proposal_operation( op ); @@ -1507,6 +1801,18 @@ namespace hive { namespace plugins { namespace condenser_api { return true; } + bool operator()( const effective_comment_vote_operation& op )const + { + l_op = legacy_effective_comment_vote_operation( op ); + return true; + } + + bool operator()( const recurrent_transfer_operation& op )const + { + l_op = legacy_recurrent_transfer_operation( op ); + return true; + } + // Should only be SMT ops template< typename T > bool operator()( const T& )const { return false; } @@ -1528,6 +1834,11 @@ struct convert_from_legacy_operation_visitor return operation( transfer_to_vesting_operation( op ) ); } + operation operator()( const legacy_transfer_to_vesting_completed_operation& op )const + { + return operation( transfer_to_vesting_completed_operation( op ) ); + } + operation operator()( const legacy_withdraw_vesting_operation& op )const { return operation( withdraw_vesting_operation( op ) ); @@ -1548,11 +1859,21 @@ struct convert_from_legacy_operation_visitor return operation( convert_operation( op ) ); } + operation operator()( const legacy_collateralized_convert_operation& op )const + { + return operation( collateralized_convert_operation( op ) ); + } + operation operator()( const legacy_account_create_operation& op )const { return operation( account_create_operation( op ) ); } + operation operator()( const legacy_account_created_operation& op )const + { + return operation( account_created_operation( op ) ); + } + operation operator()( const legacy_witness_update_operation& op )const { return operation( witness_update_operation( op ) ); @@ -1613,6 +1934,11 @@ struct convert_from_legacy_operation_visitor return operation( fill_convert_request_operation( op ) ); } + operation operator()( const legacy_fill_collateralized_convert_request_operation& op )const + { + return operation( fill_collateralized_convert_request_operation( op ) ); + } + operation operator()( const legacy_author_reward_operation& op )const { return operation( author_reward_operation( op ) ); @@ -1673,6 +1999,16 @@ struct convert_from_legacy_operation_visitor return operation( claim_account_operation( op ) ); } + operation operator()( const legacy_vesting_shares_split_operation& op )const + { + return operation( vesting_shares_split_operation( op ) ); + } + + operation operator()( const legacy_pow_reward_operation& op )const + { + return operation( pow_reward_operation( op ) ); + } + operation operator()( const legacy_create_proposal_operation& op )const { return operation( create_proposal_operation( op ) ); @@ -1703,6 +2039,16 @@ struct convert_from_legacy_operation_visitor return operation( hardfork_hive_restore_operation( op ) ); } + operation operator()( const legacy_effective_comment_vote_operation& op )const + { + return operation( effective_comment_vote_operation( op ) ); + } + + operation operator()( const legacy_recurrent_transfer_operation& op )const + { + return operation( recurrent_transfer_operation( op ) ); + } + template< typename T > operation operator()( const T& t )const { @@ -1723,6 +2069,9 @@ void from_variant( const fc::variant&, hive::plugins::condenser_api::legacy_comm void to_variant( const hive::plugins::condenser_api::legacy_pow2_work&, fc::variant& ); void from_variant( const fc::variant&, hive::plugins::condenser_api::legacy_pow2_work& ); +void to_variant( const hive::plugins::condenser_api::legacy_update_proposal_extensions&, fc::variant& ); +void from_variant( const fc::variant&, hive::plugins::condenser_api::legacy_update_proposal_extensions& ); + struct from_old_static_variant { variant& var; @@ -1766,6 +2115,20 @@ void old_sv_from_variant( const fc::variant& v, T& sv ) sv.visit( to_old_static_variant(ar[1]) ); } +template< typename T > +void new_sv_from_variant( const fc::variant& v, T& sv ) +{ + FC_ASSERT( v.is_object(), "Input data have to treated as object." ); + auto v_object = v.get_object(); + + FC_ASSERT( v_object.contains( "type" ), "Type field doesn't exist." ); + FC_ASSERT( v_object.contains( "value" ), "Value field doesn't exist." ); + + auto ar = v.get_object(); + sv.set_which( static_cast< int64_t >( ar["type"].as_uint64() ) ); + sv.visit( to_old_static_variant(ar["value"]) ); +} + // allows detection of pre-rebranding calls and special error reaction // note: that and related code will be removed some time after HF24 // since all nodes and libraries should be updated by then @@ -1821,6 +2184,7 @@ FC_REFLECT( hive::plugins::condenser_api::legacy_price, (base)(quote) ) FC_REFLECT( hive::plugins::condenser_api::legacy_transfer_to_savings_operation, (from)(to)(amount)(memo) ) FC_REFLECT( hive::plugins::condenser_api::legacy_transfer_from_savings_operation, (from)(request_id)(to)(amount)(memo) ) FC_REFLECT( hive::plugins::condenser_api::legacy_convert_operation, (owner)(requestid)(amount) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_collateralized_convert_operation, (owner)(requestid)(amount) ) FC_REFLECT( hive::plugins::condenser_api::legacy_feed_publish_operation, (publisher)(exchange_rate) ) FC_REFLECT( hive::plugins::condenser_api::legacy_account_create_operation, @@ -1833,6 +2197,12 @@ FC_REFLECT( hive::plugins::condenser_api::legacy_account_create_operation, (memo_key) (json_metadata) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_account_created_operation, + (new_account_name) + (creator) + (initial_vesting_shares) + (initial_delegation)) + FC_REFLECT( hive::plugins::condenser_api::legacy_account_create_with_delegation_operation, (fee) (delegation) @@ -1847,6 +2217,7 @@ FC_REFLECT( hive::plugins::condenser_api::legacy_account_create_with_delegation_ FC_REFLECT( hive::plugins::condenser_api::legacy_transfer_operation, (from)(to)(amount)(memo) ) FC_REFLECT( hive::plugins::condenser_api::legacy_transfer_to_vesting_operation, (from)(to)(amount) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_transfer_to_vesting_completed_operation, (from_account)(to_account)(hive_vested)(vesting_shares_received) ) FC_REFLECT( hive::plugins::condenser_api::legacy_withdraw_vesting_operation, (account)(vesting_shares) ) FC_REFLECT( hive::plugins::condenser_api::legacy_witness_update_operation, (owner)(url)(block_signing_key)(props)(fee) ) FC_REFLECT( hive::plugins::condenser_api::legacy_limit_order_create_operation, (owner)(orderid)(amount_to_sell)(min_to_receive)(fill_or_kill)(expiration) ) @@ -1870,10 +2241,11 @@ FC_REFLECT_ALIASED_NAMES( hive::plugins::condenser_api::legacy_claim_reward_bala ( ("reward_hive", "reward_steem") ) ( ("reward_hbd", "reward_sbd") ) ) FC_REFLECT( hive::plugins::condenser_api::legacy_delegate_vesting_shares_operation, (delegator)(delegatee)(vesting_shares) ) -FC_REFLECT( hive::plugins::condenser_api::legacy_author_reward_operation, (author)(permlink)(hbd_payout)(hive_payout)(vesting_payout) ) -FC_REFLECT( hive::plugins::condenser_api::legacy_curation_reward_operation, (curator)(reward)(comment_author)(comment_permlink) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_author_reward_operation, (author)(permlink)(hbd_payout)(hive_payout)(vesting_payout)(payout_must_be_claimed) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_curation_reward_operation, (curator)(reward)(comment_author)(comment_permlink)(payout_must_be_claimed) ) FC_REFLECT( hive::plugins::condenser_api::legacy_comment_reward_operation, (author)(permlink)(payout) ) FC_REFLECT( hive::plugins::condenser_api::legacy_fill_convert_request_operation, (owner)(requestid)(amount_in)(amount_out) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_fill_collateralized_convert_request_operation, (owner)(requestid)(amount_in)(amount_out)(excess_collateral) ) FC_REFLECT( hive::plugins::condenser_api::legacy_liquidity_reward_operation, (owner)(payout) ) FC_REFLECT( hive::plugins::condenser_api::legacy_interest_operation, (owner)(interest) ) FC_REFLECT( hive::plugins::condenser_api::legacy_fill_vesting_withdraw_operation, (from_account)(to_account)(withdrawn)(deposited) ) @@ -1883,11 +2255,15 @@ FC_REFLECT( hive::plugins::condenser_api::legacy_return_vesting_delegation_opera FC_REFLECT( hive::plugins::condenser_api::legacy_comment_benefactor_reward_operation, (benefactor)(author)(permlink)(hbd_payout)(hive_payout)(vesting_payout) ) FC_REFLECT( hive::plugins::condenser_api::legacy_producer_reward_operation, (producer)(vesting_shares) ) FC_REFLECT( hive::plugins::condenser_api::legacy_claim_account_operation, (creator)(fee)(extensions) ) -FC_REFLECT( hive::plugins::condenser_api::legacy_proposal_pay_operation, (receiver)(payer)(payment)(trx_id)(op_in_trx) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_vesting_shares_split_operation, (owner)(vesting_shares_before_split)(vesting_shares_after_split) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_pow_reward_operation, (worker)(reward) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_proposal_pay_operation, (proposal_id)(receiver)(payer)(payment)(trx_id)(op_in_trx) ) FC_REFLECT( hive::plugins::condenser_api::legacy_sps_fund_operation, (fund_account)(additional_funds) ) FC_REFLECT( hive::plugins::condenser_api::legacy_create_proposal_operation, (creator)(receiver)(start_date)(end_date)(daily_pay)(subject)(permlink) ) -FC_REFLECT( hive::plugins::condenser_api::legacy_update_proposal_operation, (proposal_id)(creator)(daily_pay)(subject)(permlink) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_update_proposal_operation, (proposal_id)(creator)(daily_pay)(subject)(permlink)(extensions) ) FC_REFLECT( hive::plugins::condenser_api::legacy_hardfork_hive_operation, (account)(treasury)(hbd_transferred)(hive_transferred)(vests_converted)(total_hive_from_vests) ) FC_REFLECT( hive::plugins::condenser_api::legacy_hardfork_hive_restore_operation, (account)(treasury)(hbd_transferred)(hive_transferred) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_effective_comment_vote_operation, (voter)(author)(permlink)(weight)(rshares)(total_vote_weight)(pending_payout) ) +FC_REFLECT( hive::plugins::condenser_api::legacy_recurrent_transfer_operation, (from)(to)(amount)(memo)(recurrence)(executions) ) FC_REFLECT_TYPENAME( hive::plugins::condenser_api::legacy_operation ) diff --git a/libraries/plugins/apis/database_api/database_api.cpp b/libraries/plugins/apis/database_api/database_api.cpp index c3bec8acc1..470feef953 100644 --- a/libraries/plugins/apis/database_api/database_api.cpp +++ b/libraries/plugins/apis/database_api/database_api.cpp @@ -80,6 +80,8 @@ class database_api_impl (find_vesting_delegation_expirations) (list_hbd_conversion_requests) (find_hbd_conversion_requests) + (list_collateralized_conversion_requests) + (find_collateralized_conversion_requests) (list_decline_voting_rights_requests) (find_decline_voting_rights_requests) (get_comment_pending_payouts) @@ -92,6 +94,7 @@ class database_api_impl (list_proposals) (find_proposals) (list_proposal_votes) + (find_recurrent_transfers) (get_transaction_hex) (get_required_signatures) (get_potential_signatures) @@ -107,6 +110,7 @@ class database_api_impl (list_smt_token_emissions) (find_smt_token_emissions) #endif + (is_known_transaction) ) template< typename ApiResultType, typename ResultType > @@ -148,7 +152,7 @@ class database_api_impl auto itr = idx.iterator_to(*(_db.get_index().find( id ))); auto end = idx.end(); - iteration_loop< ResultType, OnPushType, FilterType >( itr, end, result, limit, std::move(on_push), std::move(filter) ); + iteration_loop< ResultType, OnPushType, FilterType >( itr, end, result, limit, std::forward(on_push), std::forward(filter) ); } else if( direction == descending ) { @@ -156,7 +160,7 @@ class database_api_impl auto iter = boost::make_reverse_iterator( index_it ); auto iter_end = boost::make_reverse_iterator( idx.begin() ); - iteration_loop< ResultType, OnPushType, FilterType >( iter, iter_end, result, limit, std::move(on_push), std::move(filter) ); + iteration_loop< ResultType, OnPushType, FilterType >( iter, iter_end, result, limit, std::forward(on_push), std::forward(filter) ); } } @@ -172,7 +176,7 @@ class database_api_impl ) { if ( last_index.valid() ) { - iterate_results_from_index< IndexType, OrderType >( *last_index, result, limit, std::move(on_push), std::move(filter), direction ); + iterate_results_from_index< IndexType, OrderType >( *last_index, result, limit, std::forward(on_push), std::forward(filter), direction ); return; } @@ -182,14 +186,14 @@ class database_api_impl auto itr = idx.lower_bound( start ); auto end = idx.end(); - iteration_loop< ResultType, OnPushType, FilterType >( itr, end, result, limit, std::move(on_push), std::move(filter) ); + iteration_loop< ResultType, OnPushType, FilterType >( itr, end, result, limit, std::forward(on_push), std::forward(filter) ); } else if( direction == descending ) { auto iter = boost::make_reverse_iterator( idx.upper_bound(start) ); auto end_iter = boost::make_reverse_iterator( idx.begin() ); - iteration_loop< ResultType, OnPushType, FilterType >( iter, end_iter, result, limit, std::move(on_push), std::move(filter) ); + iteration_loop< ResultType, OnPushType, FilterType >( iter, end_iter, result, limit, std::forward(on_push), std::forward(filter) ); } } @@ -205,7 +209,7 @@ class database_api_impl { if( last_index.valid() ) { - iterate_results_from_index< IndexType, OrderType >( *last_index, result, limit, std::move( on_push ), std::move( filter ), direction ); + iterate_results_from_index< IndexType, OrderType >( *last_index, result, limit, std::forward(on_push), std::forward(filter), direction ); return; } @@ -215,14 +219,14 @@ class database_api_impl auto itr = idx.begin(); auto end = idx.end(); - iteration_loop< ResultType, OnPushType, FilterType >( itr, end, result, limit, std::move(on_push), std::move(filter) ); + iteration_loop< ResultType, OnPushType, FilterType >( itr, end, result, limit, std::forward(on_push), std::forward(filter) ); } else if( direction == descending ) { auto iter = boost::make_reverse_iterator( idx.end() ); auto end_iter = boost::make_reverse_iterator( idx.begin() ); - iteration_loop< ResultType, OnPushType, FilterType >( iter, end_iter, result, limit, std::move(on_push), std::move(filter) ); + iteration_loop< ResultType, OnPushType, FilterType >( iter, end_iter, result, limit, std::forward(on_push), std::forward(filter) ); } } @@ -307,7 +311,7 @@ DEFINE_API_IMPL( database_api_impl, get_reward_funds ) DEFINE_API_IMPL( database_api_impl, get_current_price_feed ) { - return _db.get_feed_history().current_median_history;; + return _db.get_feed_history().current_median_history; } DEFINE_API_IMPL( database_api_impl, get_feed_history ) @@ -468,8 +472,15 @@ DEFINE_API_IMPL( database_api_impl, list_accounts ) case( by_proxy ): { auto key = args.start.as< std::pair< account_name_type, account_name_type > >(); + account_id_type proxy_id; + if( key.first != HIVE_PROXY_TO_SELF_ACCOUNT ) + { + const auto* proxy = _db.find_account( key.first ); + FC_ASSERT( proxy != nullptr, "Given proxy account does not exist." ); + proxy_id = proxy->get_id(); + } iterate_results< chain::account_index, chain::by_proxy >( - boost::make_tuple( key.first, key.second ), + boost::make_tuple( proxy_id, key.second ), result.accounts, args.limit, [&]( const account_object& a, const database& db ){ return api_account_object( a, db, args.delayed_votes_active ); }, @@ -993,8 +1004,15 @@ DEFINE_API_IMPL( database_api_impl, list_hbd_conversion_requests ) case( by_account ): { auto key = args.start.as< std::pair< account_name_type, uint32_t > >(); + account_id_type owner_id; + if( key.first != "" ) + { + const auto* owner = _db.find_account( key.first ); + FC_ASSERT( owner != nullptr, "Given account does not exist." ); + owner_id = owner->get_id(); + } iterate_results< chain::convert_request_index, chain::by_owner >( - boost::make_tuple( key.first, key.second ), + boost::make_tuple( owner_id, key.second ), result.requests, args.limit, &database_api_impl::on_push_default< api_convert_request_object, convert_request_object >, @@ -1011,10 +1029,15 @@ DEFINE_API_IMPL( database_api_impl, list_hbd_conversion_requests ) DEFINE_API_IMPL( database_api_impl, find_hbd_conversion_requests ) { find_hbd_conversion_requests_return result; + + const auto* owner = _db.find_account( args.account ); + FC_ASSERT( owner != nullptr, "Given account does not exist." ); + account_id_type owner_id = owner->get_id(); + const auto& convert_idx = _db.get_index< chain::convert_request_index, chain::by_owner >(); - auto itr = convert_idx.lower_bound( args.account ); + auto itr = convert_idx.lower_bound( owner_id ); - while( itr != convert_idx.end() && itr->owner == args.account && result.requests.size() <= DATABASE_API_SINGLE_QUERY_LIMIT ) + while( itr != convert_idx.end() && itr->get_owner() == owner_id && result.requests.size() <= DATABASE_API_SINGLE_QUERY_LIMIT ) { result.requests.emplace_back( *itr, _db ); ++itr; @@ -1023,6 +1046,72 @@ DEFINE_API_IMPL( database_api_impl, find_hbd_conversion_requests ) return result; } +/* Collateralized Conversion Requests */ + +DEFINE_API_IMPL( database_api_impl, list_collateralized_conversion_requests ) +{ + FC_ASSERT( args.limit <= DATABASE_API_SINGLE_QUERY_LIMIT ); + + list_collateralized_conversion_requests_return result; + result.requests.reserve( args.limit ); + + switch( args.order ) + { + case( by_conversion_date ): + { + auto key = args.start.as< std::pair< time_point_sec, collateralized_convert_request_id_type > >(); + iterate_results< chain::collateralized_convert_request_index, chain::by_conversion_date >( + boost::make_tuple( key.first, key.second ), + result.requests, + args.limit, + &database_api_impl::on_push_default< api_collateralized_convert_request_object, collateralized_convert_request_object >, + &database_api_impl::filter_default< collateralized_convert_request_object > ); + break; + } + case( by_account ): + { + auto key = args.start.as< std::pair< account_name_type, uint32_t > >(); + account_id_type owner_id; + if( key.first != "" ) + { + const auto* owner = _db.find_account( key.first ); + FC_ASSERT( owner != nullptr, "Given account does not exist." ); + owner_id = owner->get_id(); + } + iterate_results< chain::collateralized_convert_request_index, chain::by_owner >( + boost::make_tuple( owner_id, key.second ), + result.requests, + args.limit, + &database_api_impl::on_push_default< api_collateralized_convert_request_object, collateralized_convert_request_object >, + &database_api_impl::filter_default< collateralized_convert_request_object > ); + break; + } + default: + FC_ASSERT( false, "Unknown or unsupported sort order" ); + } + + return result; +} + +DEFINE_API_IMPL( database_api_impl, find_collateralized_conversion_requests ) +{ + find_collateralized_conversion_requests_return result; + + const auto* owner = _db.find_account( args.account ); + FC_ASSERT( owner != nullptr, "Given account does not exist." ); + account_id_type owner_id = owner->get_id(); + + const auto& convert_idx = _db.get_index< chain::collateralized_convert_request_index, chain::by_owner >(); + auto itr = convert_idx.lower_bound( owner_id ); + + while( itr != convert_idx.end() && itr->get_owner() == owner_id && result.requests.size() <= DATABASE_API_SINGLE_QUERY_LIMIT ) + { + result.requests.emplace_back( *itr, _db ); + ++itr; + } + + return result; +} /* Decline Voting Rights Requests */ @@ -1109,7 +1198,7 @@ DEFINE_API_IMPL(database_api_impl, get_comment_pending_payouts) } } - return std::move(retval); + return retval; } /* Comments */ @@ -1533,6 +1622,26 @@ DEFINE_API_IMPL( database_api_impl, list_proposal_votes ) return result; } +/* Find recurrent transfers */ + +DEFINE_API_IMPL( database_api_impl, find_recurrent_transfers ) { + find_recurrent_transfers_return result; + + const auto* from_account = _db.find_account( args.from ); + FC_ASSERT( from_account != nullptr, "Given 'from' account does not exist." ); + auto from_account_id = from_account->get_id(); + + const auto &idx = _db.get_index(); + auto itr = idx.find(from_account_id); + + while (itr != idx.end() && itr->from_id == from_account_id && result.recurrent_transfers.size() <= DATABASE_API_SINGLE_QUERY_LIMIT) { + const auto& to_account = _db.get_account( itr->to_id ); + result.recurrent_transfers.emplace_back(*itr, from_account->name, to_account.name); + ++itr; + } + + return result; +} ////////////////////////////////////////////////////////////////////// // // @@ -1716,7 +1825,7 @@ DEFINE_API_IMPL( database_api_impl, list_smt_contributions ) &database_api_impl::filter_default< chain::smt_contribution_object > ); break; } -#ifndef IS_LOW_MEM +// #ifndef IS_LOW_MEM // indexing by contributor might cause optimization problems in the future case ( by_contributor ): { auto key = args.start.get_array(); @@ -1736,7 +1845,7 @@ DEFINE_API_IMPL( database_api_impl, list_smt_contributions ) &database_api_impl::filter_default< chain::smt_contribution_object > ); break; } -#endif +// #endif default: FC_ASSERT( false, "Unknown or unsupported sort order" ); } @@ -1894,6 +2003,13 @@ DEFINE_API_IMPL( database_api_impl, find_smt_token_emissions ) #endif +DEFINE_API_IMPL( database_api_impl, is_known_transaction ) +{ + is_known_transaction_return result; + result.is_known = _db.is_known_transaction(args.id); + return result; +} + DEFINE_LOCKLESS_APIS( database_api, (get_config)(get_version) ) DEFINE_READ_APIS( database_api, @@ -1927,6 +2043,8 @@ DEFINE_READ_APIS( database_api, (find_vesting_delegation_expirations) (list_hbd_conversion_requests) (find_hbd_conversion_requests) + (list_collateralized_conversion_requests) + (find_collateralized_conversion_requests) (list_decline_voting_rights_requests) (find_decline_voting_rights_requests) (get_comment_pending_payouts) @@ -1939,6 +2057,7 @@ DEFINE_READ_APIS( database_api, (find_proposals) (list_proposal_votes) (get_order_book) + (find_recurrent_transfers) (get_transaction_hex) (get_required_signatures) (get_potential_signatures) @@ -1954,6 +2073,7 @@ DEFINE_READ_APIS( database_api, (list_smt_token_emissions) (find_smt_token_emissions) #endif + (is_known_transaction) ) } } } // hive::plugins::database_api diff --git a/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api.hpp b/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api.hpp index 12fae9edb4..40e64d0b18 100644 --- a/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api.hpp +++ b/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api.hpp @@ -83,6 +83,8 @@ class database_api (find_vesting_delegation_expirations) (list_hbd_conversion_requests) (find_hbd_conversion_requests) + (list_collateralized_conversion_requests) + (find_collateralized_conversion_requests) (list_decline_voting_rights_requests) (find_decline_voting_rights_requests) @@ -109,6 +111,11 @@ class database_api (find_proposals) (list_proposal_votes) + //////////////////////// + // Recurrent payments // + //////////////////////// + (find_recurrent_transfers) + //////////////////////////// // Authority / validation // //////////////////////////// @@ -160,6 +167,7 @@ class database_api (list_smt_token_emissions) (find_smt_token_emissions) #endif + (is_known_transaction) ) private: diff --git a/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api_args.hpp b/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api_args.hpp index 87e5e5b51f..a7f214e062 100644 --- a/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api_args.hpp +++ b/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api_args.hpp @@ -55,7 +55,7 @@ enum sort_order_type by_voter_proposal, by_proposal_voter, by_contributor, - by_symbol_id + by_symbol_id, }; enum order_direction_type @@ -359,6 +359,24 @@ struct find_hbd_conversion_requests_args typedef list_hbd_conversion_requests_return find_hbd_conversion_requests_return; +/* Collateralized Converstions */ + +typedef list_object_args_type list_collateralized_conversion_requests_args; + +struct list_collateralized_conversion_requests_return +{ + vector< api_collateralized_convert_request_object > requests; +}; + + +struct find_collateralized_conversion_requests_args +{ + account_name_type account; +}; + +typedef list_collateralized_conversion_requests_return find_collateralized_conversion_requests_return; + + /* Decline Voting Rights Requests */ typedef list_object_args_type list_decline_voting_rights_requests_args; @@ -505,6 +523,20 @@ struct list_proposal_votes_return vector< api_proposal_vote_object > proposal_votes; }; +/* Recurrent transfers */ + +struct find_recurrent_transfers_args +{ + account_name_type from; +}; + +struct find_recurrent_transfers_return +{ + vector< api_recurrent_transfer_object > recurrent_transfers; +}; + + +/* Transactions */ struct get_transaction_hex_args { @@ -647,6 +679,16 @@ typedef list_smt_token_emissions_return find_smt_token_emissions_return; #endif +struct is_known_transaction_args +{ + transaction_id_type id; +}; + +struct is_known_transaction_return +{ + bool is_known = false; +}; + } } } // hive::database_api FC_REFLECT( hive::plugins::database_api::get_version_return, @@ -782,6 +824,12 @@ FC_REFLECT( hive::plugins::database_api::list_hbd_conversion_requests_return, FC_REFLECT( hive::plugins::database_api::find_hbd_conversion_requests_args, (account) ) +FC_REFLECT( hive::plugins::database_api::list_collateralized_conversion_requests_return, + (requests) ) + +FC_REFLECT( hive::plugins::database_api::find_collateralized_conversion_requests_args, + (account) ) + FC_REFLECT( hive::plugins::database_api::list_decline_voting_rights_requests_return, (requests) ) @@ -835,6 +883,12 @@ FC_REFLECT( hive::plugins::database_api::find_proposals_args, FC_REFLECT( hive::plugins::database_api::list_proposal_votes_return, (proposal_votes) ) +FC_REFLECT( hive::plugins::database_api::find_recurrent_transfers_return, + (recurrent_transfers) ) + +FC_REFLECT( hive::plugins::database_api::find_recurrent_transfers_args, + (from) ) + FC_REFLECT( hive::plugins::database_api::get_transaction_hex_args, (trx) ) @@ -896,3 +950,9 @@ FC_REFLECT( hive::plugins::database_api::find_smt_token_emissions_args, (asset_symbol) ) #endif + +FC_REFLECT( hive::plugins::database_api::is_known_transaction_args, + (id) ) + +FC_REFLECT( hive::plugins::database_api::is_known_transaction_return, + (is_known) ) diff --git a/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api_objects.hpp b/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api_objects.hpp index 439a91c069..8fc8b0993b 100644 --- a/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api_objects.hpp +++ b/libraries/plugins/apis/database_api/include/hive/plugins/database_api/database_api_objects.hpp @@ -143,10 +143,10 @@ struct api_convert_request_object { api_convert_request_object( const convert_request_object& o, const database& db ): id( o.get_id() ), - owner( o.owner ), - requestid( o.requestid ), - amount( o.amount ), - conversion_date( o.conversion_date ) + owner( db.get_account( o.get_owner() ).get_name() ), + requestid( o.get_request_id() ), + amount( o.get_convert_amount() ), + conversion_date( o.get_conversion_date() ) {} convert_request_id_type id; @@ -156,6 +156,25 @@ struct api_convert_request_object time_point_sec conversion_date; }; +struct api_collateralized_convert_request_object +{ + api_collateralized_convert_request_object( const collateralized_convert_request_object& o, const database& db ) : + id( o.get_id() ), + owner( db.get_account( o.get_owner() ).get_name() ), + requestid( o.get_request_id() ), + collateral_amount( o.get_collateral_amount() ), + converted_amount( o.get_converted_amount() ), + conversion_date( o.get_conversion_date() ) + {} + + collateralized_convert_request_id_type id; + account_name_type owner; + uint32_t requestid; + asset collateral_amount; + asset converted_amount; + time_point_sec conversion_date; +}; + struct api_decline_voting_rights_request_object { api_decline_voting_rights_request_object( const decline_voting_rights_request_object& o, const database& db ): @@ -217,7 +236,7 @@ struct api_dynamic_global_property_object current_aslot( o.current_aslot ), recent_slots_filled( o.recent_slots_filled ), participation_count( o.participation_count ), - last_irreversible_block_num( o.last_irreversible_block_num ), + last_irreversible_block_num( db.get_last_irreversible_block_num()), vote_power_reserve_rate( o.vote_power_reserve_rate ), delegation_return_period( o.delegation_return_period ), reverse_auction_seconds( o.reverse_auction_seconds ), @@ -231,7 +250,14 @@ struct api_dynamic_global_property_object vesting_reward_percent( o.vesting_reward_percent ), sps_fund_percent( o.sps_fund_percent ), sps_interval_ledger( o.sps_interval_ledger ), - downvote_pool_percent( o.downvote_pool_percent ) + downvote_pool_percent( o.downvote_pool_percent ), + current_remove_threshold( o.current_remove_threshold ), + early_voting_seconds( o.early_voting_seconds ), + mid_voting_seconds( o.mid_voting_seconds ), + max_consecutive_recurrent_transfer_failures( o.max_consecutive_recurrent_transfer_failures ), + max_recurrent_transfer_end_date( o.max_recurrent_transfer_end_date ), + min_recurrent_transfers_recurrence( o.min_recurrent_transfers_recurrence ), + max_open_recurrent_transfers( o.max_open_recurrent_transfers ) #ifdef HIVE_ENABLE_SMT , smt_creation_fee( o.smt_creation_fee ) #endif @@ -240,12 +266,12 @@ struct api_dynamic_global_property_object api_dynamic_global_property_object() {} dynamic_global_property_id_type id; - uint32_t head_block_number; + uint32_t head_block_number = 0; block_id_type head_block_id; time_point_sec time; account_name_type current_witness; - uint64_t total_pow; - uint32_t num_pow_witnesses; + uint64_t total_pow = 0; + uint32_t num_pow_witnesses = 0; asset virtual_supply; asset current_supply; asset init_hbd_supply; @@ -256,28 +282,35 @@ struct api_dynamic_global_property_object fc::uint128 total_reward_shares2; asset pending_rewarded_vesting_shares; asset pending_rewarded_vesting_hive; - uint16_t hbd_interest_rate; - uint16_t hbd_print_rate; - uint32_t maximum_block_size; - uint16_t required_actions_partition_percent; - uint64_t current_aslot; + uint16_t hbd_interest_rate = 0; + uint16_t hbd_print_rate = 0; + uint32_t maximum_block_size = 0; + uint16_t required_actions_partition_percent = 0; + uint64_t current_aslot = 0; fc::uint128_t recent_slots_filled; - uint8_t participation_count; - uint32_t last_irreversible_block_num; - uint32_t vote_power_reserve_rate; - uint32_t delegation_return_period; - uint64_t reverse_auction_seconds; - int64_t available_account_subsidies; - uint16_t hbd_stop_percent; - uint16_t hbd_start_percent; + uint8_t participation_count = 0; + uint32_t last_irreversible_block_num = 0; + uint32_t vote_power_reserve_rate = 0; + uint32_t delegation_return_period = 0; + uint64_t reverse_auction_seconds = 0; + int64_t available_account_subsidies = 0; + uint16_t hbd_stop_percent = 0; + uint16_t hbd_start_percent = 0; time_point_sec next_maintenance_time; time_point_sec last_budget_time; time_point_sec next_daily_maintenance_time; - uint16_t content_reward_percent; - uint16_t vesting_reward_percent; - uint16_t sps_fund_percent; + uint16_t content_reward_percent = 0; + uint16_t vesting_reward_percent = 0; + uint16_t sps_fund_percent = 0; asset sps_interval_ledger; - uint16_t downvote_pool_percent; + uint16_t downvote_pool_percent = 0; + int16_t current_remove_threshold = 0; + uint64_t early_voting_seconds = 0; + uint64_t mid_voting_seconds = 0; + uint8_t max_consecutive_recurrent_transfer_failures = HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES; + uint16_t max_recurrent_transfer_end_date = HIVE_MAX_RECURRENT_TRANSFER_END_DATE; + uint8_t min_recurrent_transfers_recurrence = HIVE_MIN_RECURRENT_TRANSFERS_RECURRENCE; + uint16_t max_open_recurrent_transfers = HIVE_MAX_OPEN_RECURRENT_TRANSFERS; #ifdef HIVE_ENABLE_SMT asset smt_creation_fee; #endif @@ -287,9 +320,9 @@ struct api_change_recovery_account_request_object { api_change_recovery_account_request_object( const change_recovery_account_request_object& o, const database& db ): id( o.get_id() ), - account_to_recover( o.account_to_recover ), - recovery_account( o.recovery_account ), - effective_on( o.effective_on ) + account_to_recover( o.get_account_to_recover() ), + recovery_account( o.get_recovery_account() ), + effective_on( o.get_execution_time() ) {} change_recovery_account_request_id_type id; @@ -365,12 +398,6 @@ struct api_comment_object } -#if !defined(IS_LOW_MEM) && defined(STORE_COMMENT_CONTENT) - const auto& con = db.get< chain::comment_content_object, chain::by_comment >( o.get_id() ); - title = to_string( con.title ); - body = to_string( con.body ); - json_metadata = to_string( con.json_metadata ); -#endif } api_comment_object(){} @@ -455,14 +482,14 @@ struct api_account_object { api_account_object( const account_object& a, const database& db, bool delayed_votes_active ) : id( a.get_id() ), - name( a.name ), + name( a.get_name() ), memo_key( a.memo_key ), - proxy( a.proxy ), + proxy( HIVE_PROXY_TO_SELF_ACCOUNT ), last_account_update( a.last_account_update ), created( a.created ), mined( a.mined ), - recovery_account( a.recovery_account ), - reset_account( a.reset_account ), + recovery_account( a.get_recovery_account() ), + reset_account( HIVE_NULL_ACCOUNT ), last_account_recovery( a.last_account_recovery ), comment_count( a.comment_count ), lifetime_vote_count( a.lifetime_vote_count ), @@ -502,8 +529,13 @@ struct api_account_object last_post_edit( a.last_post_edit ), last_vote_time( a.last_vote_time ), post_bandwidth( a.post_bandwidth ), - pending_claimed_accounts( a.pending_claimed_accounts ) + pending_claimed_accounts( a.pending_claimed_accounts ), + open_recurrent_transfers( a.open_recurrent_transfers ), + governance_vote_expiration_ts( a.get_governance_vote_expiration_ts()) { + if( a.has_proxy() ) + proxy = db.get_account( a.get_proxy() ).get_name(); + size_t n = a.proxied_vsf_votes.size(); proxied_vsf_votes.reserve( n ); for( size_t i=0; i > delayed_votes; + time_point_sec governance_vote_expiration_ts; }; struct api_owner_authority_history_object @@ -639,9 +673,9 @@ struct api_account_recovery_request_object { api_account_recovery_request_object( const account_recovery_request_object& o, const database& db ) : id( o.get_id() ), - account_to_recover( o.account_to_recover ), - new_owner_authority( authority( o.new_owner_authority ) ), - expires( o.expires ) + account_to_recover( o.get_account_to_recover() ), + new_owner_authority( authority( o.get_new_owner_authority() ) ), + expires( o.get_expiration_time() ) {} api_account_recovery_request_object() {} @@ -685,6 +719,9 @@ struct api_feed_history_object api_feed_history_object( const feed_history_object& f ) : id( f.get_id() ), current_median_history( f.current_median_history ), + market_median_history( f.market_median_history ), + current_min_history( f.current_min_history ), + current_max_history( f.current_max_history ), price_history( f.price_history.begin(), f.price_history.end() ) {} @@ -692,6 +729,9 @@ struct api_feed_history_object feed_history_id_type id; price current_median_history; + price market_median_history; + price current_min_history; + price current_max_history; deque< price > price_history; }; @@ -850,7 +890,7 @@ struct api_smt_token_object api_smt_token_object( const smt_token_object& token, const database& db ) : token( token.copy_chain_object() ) //FIXME: exposes internal chain object as API result { - const smt_ico_object* ico = db.find< chain::smt_ico_object, chain::by_symbol >( token.liquid_symbol ); + const smt_ico_object* ico = db.find< smt_ico_object, by_symbol >( token.liquid_symbol ); if ( ico != nullptr ) this->ico = ico->copy_chain_object(); //FIXME: exposes internal chain object as API result } @@ -971,6 +1011,35 @@ struct api_proposal_vote_object api_proposal_object proposal; }; + +struct api_recurrent_transfer_object +{ + api_recurrent_transfer_object() = default; + + api_recurrent_transfer_object( const recurrent_transfer_object& o, const account_name_type from_name, const account_name_type to_name ): + id( o.get_id() ), + trigger_date( o.get_trigger_date() ), + from( from_name ), + to( to_name ), + amount( o.amount ), + memo( to_string(o.memo) ), + recurrence( o.recurrence ), + consecutive_failures( o.consecutive_failures ), + remaining_executions( o.remaining_executions ) + {} + + recurrent_transfer_id_type id; + time_point_sec trigger_date; + account_name_type from; + account_name_type to; + asset amount; + string memo; + uint16_t recurrence = 0; + uint8_t consecutive_failures = 0; + uint16_t remaining_executions = 0; +}; + + struct order { price order_price; @@ -1045,6 +1114,10 @@ FC_REFLECT( hive::plugins::database_api::api_convert_request_object, (id)(owner)(requestid)(amount)(conversion_date) ) +FC_REFLECT( hive::plugins::database_api::api_collateralized_convert_request_object, + (id)(owner)(requestid)(collateral_amount)(converted_amount)(conversion_date) + ) + FC_REFLECT( hive::plugins::database_api::api_decline_voting_rights_request_object, (id)(account)(effective_date) ) @@ -1063,7 +1136,9 @@ FC_REFLECT( hive::plugins::database_api::api_dynamic_global_property_object, (vote_power_reserve_rate)(delegation_return_period)(reverse_auction_seconds) (available_account_subsidies)(hbd_stop_percent)(hbd_start_percent)(next_maintenance_time) (last_budget_time)(next_daily_maintenance_time)(content_reward_percent)(vesting_reward_percent)(sps_fund_percent) - (sps_interval_ledger)(downvote_pool_percent) + (sps_interval_ledger)(downvote_pool_percent)(current_remove_threshold)(early_voting_seconds)(mid_voting_seconds) + (max_consecutive_recurrent_transfer_failures)(max_recurrent_transfer_end_date)(min_recurrent_transfers_recurrence) + (max_open_recurrent_transfers) #ifdef HIVE_ENABLE_SMT (smt_creation_fee) #endif @@ -1105,9 +1180,10 @@ FC_REFLECT( hive::plugins::database_api::api_account_object, (posting_rewards) (proxied_vsf_votes)(witnesses_voted_for) (last_post)(last_root_post)(last_post_edit)(last_vote_time) - (post_bandwidth)(pending_claimed_accounts) + (post_bandwidth)(pending_claimed_accounts)(open_recurrent_transfers) (is_smt) - (delayed_votes) + (delayed_votes) + (governance_vote_expiration_ts) ) FC_REFLECT( hive::plugins::database_api::api_owner_authority_history_object, @@ -1137,6 +1213,9 @@ FC_REFLECT( hive::plugins::database_api::api_savings_withdraw_object, FC_REFLECT( hive::plugins::database_api::api_feed_history_object, (id) (current_median_history) + (market_median_history) + (current_min_history) + (current_max_history) (price_history) ) @@ -1240,3 +1319,4 @@ FC_REFLECT( hive::plugins::database_api::api_proposal_vote_object, FC_REFLECT( hive::plugins::database_api::order, (order_price)(real_price)(hive)(hbd)(created) ); FC_REFLECT( hive::plugins::database_api::order_book, (asks)(bids) ); +FC_REFLECT(hive::plugins::database_api::api_recurrent_transfer_object, (id)(trigger_date)(from)(to)(amount)(memo)(recurrence)(consecutive_failures)(remaining_executions)) diff --git a/libraries/plugins/apis/network_node_api/CMakeLists.txt b/libraries/plugins/apis/network_node_api/CMakeLists.txt new file mode 100644 index 0000000000..0d6ae63cef --- /dev/null +++ b/libraries/plugins/apis/network_node_api/CMakeLists.txt @@ -0,0 +1,24 @@ +file(GLOB HEADERS "include/hive/plugins/network_node_api_plugin/*.hpp") +add_library( network_node_api_plugin + network_node_api.cpp + network_node_api_plugin.cpp + ${HEADERS} ) + +target_link_libraries( network_node_api_plugin chain_plugin json_rpc_plugin p2p_plugin appbase ) +target_include_directories( network_node_api_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" ) + +if( CLANG_TIDY_EXE ) + set_target_properties( + network_node_api_plugin PROPERTIES + CXX_CLANG_TIDY "${DO_CLANG_TIDY}" + ) +endif( CLANG_TIDY_EXE ) + +install( TARGETS + network_node_api_plugin + + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) +install( FILES ${HEADERS} DESTINATION "include/hive/network_node_api_plugin" ) diff --git a/libraries/plugins/apis/network_node_api/include/hive/plugins/network_node_api/network_node_api.hpp b/libraries/plugins/apis/network_node_api/include/hive/plugins/network_node_api/network_node_api.hpp new file mode 100644 index 0000000000..58453734b5 --- /dev/null +++ b/libraries/plugins/apis/network_node_api/include/hive/plugins/network_node_api/network_node_api.hpp @@ -0,0 +1,81 @@ +#pragma once +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +namespace hive { namespace plugins { namespace network_node_api { + +using std::vector; +using fc::variant; +using fc::optional; +using hive::plugins::json_rpc::void_type; +using hive::plugins::p2p::api_peer_status; + +/* get_info */ + +typedef void_type get_info_args; +typedef fc::variant_object get_info_return; + +/* add_node */ + +struct add_node_args +{ + fc::ip::endpoint endpoint; +}; + +typedef void_type add_node_return; + +/* set_allowed_peers */ + +struct set_allowed_peers_args +{ + std::vector< graphene::net::node_id_t > allowed_peers; +}; + +typedef void_type set_allowed_peers_return; + +/* get_connected_peers */ +typedef void_type get_connected_peers_args; +struct get_connected_peers_return +{ + std::vector< api_peer_status > connected_peers; +}; + +namespace detail{ class network_node_api_impl; } + +class network_node_api +{ + public: + network_node_api(); + ~network_node_api(); + + DECLARE_API( + (get_info) + (add_node) + (set_allowed_peers) + (get_connected_peers) + ) + + private: + std::unique_ptr< detail::network_node_api_impl > my; +}; + +} } } // hive::plugins::network_node_api + +FC_REFLECT( hive::plugins::network_node_api::add_node_args, + (endpoint) ) +FC_REFLECT( hive::plugins::network_node_api::set_allowed_peers_args, + (allowed_peers) ) +FC_REFLECT( hive::plugins::network_node_api::get_connected_peers_return, + (connected_peers) ) diff --git a/libraries/plugins/apis/network_node_api/include/hive/plugins/network_node_api/network_node_api_plugin.hpp b/libraries/plugins/apis/network_node_api/include/hive/plugins/network_node_api/network_node_api_plugin.hpp new file mode 100644 index 0000000000..69dd625830 --- /dev/null +++ b/libraries/plugins/apis/network_node_api/include/hive/plugins/network_node_api/network_node_api_plugin.hpp @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include + +#include + +#define HIVE_NETWORK_NODE_API_PLUGIN_NAME "network_node_api" + +namespace hive { namespace plugins { namespace network_node_api { + +using namespace appbase; + +class network_node_api_plugin : public appbase::plugin< network_node_api_plugin > +{ +public: + APPBASE_PLUGIN_REQUIRES( + (hive::plugins::json_rpc::json_rpc_plugin) + (hive::plugins::chain::chain_plugin) + (hive::plugins::p2p::p2p_plugin) + ) + + network_node_api_plugin(); + virtual ~network_node_api_plugin(); + + static const std::string& name() { static std::string name = HIVE_NETWORK_NODE_API_PLUGIN_NAME; return name; } + + virtual void set_program_options( options_description& cli, options_description& cfg ) override; + virtual void plugin_initialize( const variables_map& options ) override; + virtual void plugin_startup() override; + virtual void plugin_shutdown() override; + + std::shared_ptr< class network_node_api > api; +}; + +} } } // hive::plugins::network_node_api diff --git a/libraries/plugins/apis/network_node_api/network_node_api.cpp b/libraries/plugins/apis/network_node_api/network_node_api.cpp new file mode 100644 index 0000000000..3e2783a039 --- /dev/null +++ b/libraries/plugins/apis/network_node_api/network_node_api.cpp @@ -0,0 +1,71 @@ +#include +#include + +#include + +namespace hive { namespace plugins { namespace network_node_api { + +namespace detail +{ + class network_node_api_impl + { + public: + network_node_api_impl() : + _p2p( appbase::app().get_plugin< hive::plugins::p2p::p2p_plugin >() ) + //_chain( appbase::app().get_plugin< hive::plugins::chain::chain_plugin >() ) + {} + + DECLARE_API_IMPL( + (get_info) + (add_node) + (set_allowed_peers) + (get_connected_peers) + ) + + hive::plugins::p2p::p2p_plugin& _p2p; + //hive::plugins::chain::chain_plugin& _chain; + }; + + DEFINE_API_IMPL( network_node_api_impl, get_info ) + { + return _p2p.get_info(); + } + + DEFINE_API_IMPL( network_node_api_impl, add_node ) + { + _p2p.add_node(args.endpoint); + add_node_return result; + return result; + } + + DEFINE_API_IMPL( network_node_api_impl, set_allowed_peers ) + { + _p2p.set_allowed_peers(args.allowed_peers); + set_allowed_peers_return result; + return result; + } + + DEFINE_API_IMPL( network_node_api_impl, get_connected_peers ) + { + get_connected_peers_return result; + result.connected_peers = _p2p.get_connected_peers(); + return result; + } + +} // detail + +network_node_api::network_node_api() : my( new detail::network_node_api_impl() ) +{ + JSON_RPC_REGISTER_API( HIVE_NETWORK_NODE_API_PLUGIN_NAME ); +} + +network_node_api::~network_node_api() {} + +DEFINE_LOCKLESS_APIS( network_node_api, + (get_info) + (add_node) + (set_allowed_peers) + (get_connected_peers) +) + +} } } // hive::plugins::network_node_api diff --git a/libraries/plugins/apis/network_node_api/network_node_api_plugin.cpp b/libraries/plugins/apis/network_node_api/network_node_api_plugin.cpp new file mode 100644 index 0000000000..0970ce0647 --- /dev/null +++ b/libraries/plugins/apis/network_node_api/network_node_api_plugin.cpp @@ -0,0 +1,19 @@ +#include +#include + +namespace hive { namespace plugins { namespace network_node_api { + +network_node_api_plugin::network_node_api_plugin() {} +network_node_api_plugin::~network_node_api_plugin() {} + +void network_node_api_plugin::set_program_options( options_description& cli, options_description& cfg ) {} + +void network_node_api_plugin::plugin_initialize( const variables_map& options ) +{ + api = std::make_shared< network_node_api >(); +} + +void network_node_api_plugin::plugin_startup() {} +void network_node_api_plugin::plugin_shutdown() {} + +} } } // hive::plugins::test_api diff --git a/libraries/plugins/apis/network_node_api/plugin.json b/libraries/plugins/apis/network_node_api/plugin.json new file mode 100644 index 0000000000..f47abeb788 --- /dev/null +++ b/libraries/plugins/apis/network_node_api/plugin.json @@ -0,0 +1,5 @@ +{ + "plugin_name": "network_node_api", + "plugin_namespace": "network_node_api", + "plugin_project": "network_node_api_plugin" +} diff --git a/libraries/plugins/apis/tags_api/include/hive/plugins/tags_api/tags_api.hpp b/libraries/plugins/apis/tags_api/include/hive/plugins/tags_api/tags_api.hpp index f9c91e52cd..1db52f04c7 100644 --- a/libraries/plugins/apis/tags_api/include/hive/plugins/tags_api/tags_api.hpp +++ b/libraries/plugins/apis/tags_api/include/hive/plugins/tags_api/tags_api.hpp @@ -127,7 +127,7 @@ struct discussion_query struct discussion_query_result { - vector< discussion > discussions; + vector< hive::plugins::tags::discussion > discussions; }; typedef get_discussion_args get_content_replies_args; diff --git a/libraries/plugins/apis/transaction_status_api/transaction_status_api.cpp b/libraries/plugins/apis/transaction_status_api/transaction_status_api.cpp index 7dbd5226d0..9cf6a159be 100644 --- a/libraries/plugins/apis/transaction_status_api/transaction_status_api.cpp +++ b/libraries/plugins/apis/transaction_status_api/transaction_status_api.cpp @@ -27,7 +27,7 @@ DEFINE_API_IMPL( transaction_status_api_impl, find_transaction ) // Have we begun tracking? if ( _db.head_block_num() >= earliest_tracked_block_num ) { - auto last_irreversible_block_num = _db.get_dynamic_global_properties().last_irreversible_block_num; + auto last_irreversible_block_num = _db.get_last_irreversible_block_num(); auto tso = _db.find< transaction_status::transaction_status_object, transaction_status::by_trx_id >( args.transaction_id ); // If we are actively tracking this transaction diff --git a/libraries/plugins/block_data_export/block_data_export_plugin.cpp b/libraries/plugins/block_data_export/block_data_export_plugin.cpp index 4456f81fa9..f3f85732d9 100644 --- a/libraries/plugins/block_data_export/block_data_export_plugin.cpp +++ b/libraries/plugins/block_data_export/block_data_export_plugin.cpp @@ -1,6 +1,9 @@ #define BOOST_THREAD_PROVIDES_EXECUTORS #define BOOST_THREAD_PROVIDES_FUTURE +#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION +#define BOOST_THREAD_USES_MOVE + #include #include diff --git a/libraries/plugins/block_log_info/block_log_info_plugin.cpp b/libraries/plugins/block_log_info/block_log_info_plugin.cpp index 2eee03323f..1a899f5982 100644 --- a/libraries/plugins/block_log_info/block_log_info_plugin.cpp +++ b/libraries/plugins/block_log_info/block_log_info_plugin.cpp @@ -75,15 +75,13 @@ void block_log_info_plugin_impl::on_post_apply_block( const block_notification& } } - const dynamic_global_property_object& dgpo = _db.get_dynamic_global_properties(); - const auto& idx = _db.get_index< block_log_pending_message_index, by_id >(); while( true ) { auto it = idx.begin(); if( it == idx.end() ) break; - if( it->data.block_num > dgpo.last_irreversible_block_num ) + if( it->data.block_num > _db.get_last_irreversible_block_num()) break; print_message( it->data ); _db.remove( *it ); diff --git a/libraries/plugins/chain/chain_plugin.cpp b/libraries/plugins/chain/chain_plugin.cpp index 628c7374fc..090a58a940 100644 --- a/libraries/plugins/chain/chain_plugin.cpp +++ b/libraries/plugins/chain/chain_plugin.cpp @@ -16,7 +16,7 @@ #include #include #include -#include + #include #include @@ -31,7 +31,6 @@ using namespace hive; using fc::flat_map; using hive::chain::block_id_type; -namespace asio = boost::asio; using hive::plugins::chain::synchronization_type; using index_memory_details_cntr_t = hive::utilities::benchmark_dumper::index_memory_details_cntr_t; @@ -82,7 +81,7 @@ class chain_plugin_impl void start_write_processing(); void stop_write_processing(); - bool start_replay_processing( synchronization_type& on_sync ); + bool start_replay_processing(); void initial_settings(); void open(); @@ -97,7 +96,6 @@ class chain_plugin_impl uint64_t shared_memory_size = 0; uint16_t shared_file_full_threshold = 0; uint16_t shared_file_scale_rate = 0; - int16_t sps_remove_threshold = -1; uint32_t chainbase_flags = 0; bfs::path shared_memory_dir; bool replay = false; @@ -131,13 +129,14 @@ class chain_plugin_impl database db; std::string block_generator_registrant; std::shared_ptr< abstract_block_producer > block_generator; - + hive::utilities::benchmark_dumper dumper; hive::chain::open_args db_open_args; get_indexes_memory_details_type get_indexes_memory_details; state_snapshot_provider* snapshot_provider = nullptr; bool is_p2p_enabled = true; + std::atomic peer_count; }; struct write_request_visitor @@ -250,6 +249,9 @@ void chain_plugin_impl::start_write_processing() { write_processor_thread = std::make_shared< std::thread >( [&]() { + ilog("Write processing thread started."); + + const fc::microseconds block_wait_max_time = fc::seconds(10*HIVE_BLOCK_INTERVAL); bool is_syncing = true; write_context* cxt; write_request_visitor req_visitor; @@ -277,18 +279,23 @@ void chain_plugin_impl::start_write_processing() * in. When the node is live the rate at which writes come in is slower and busy waiting is * not an optimal use of system resources when we could give CPU time to read threads. */ + fc::time_point last_popped_block_time = fc::time_point::now(); + fc::time_point last_msg_time = last_popped_block_time; + while( running ) { if( write_queue.pop( cxt ) ) { - fc::time_point write_lock_request_time = fc::time_point::now(); + last_popped_block_time = fc::time_point::now(); + + fc::time_point write_lock_request_time = fc::time_point::now(); db.with_write_lock( [&]() { fc::time_point write_lock_acquired_time = fc::time_point::now(); fc::microseconds write_lock_acquisition_time = write_lock_acquired_time - write_lock_request_time; if( write_lock_acquisition_time > fc::milliseconds( 50 ) ) { - wlog("write_lock_acquisition_time = ${write_lock_aquisition_time}μs exceeds warning threshold of 50ms", + wlog("write_lock_acquisition_time = ${write_lock_aquisition_time}μs exceeds warning threshold of 50ms", ("write_lock_aquisition_time", write_lock_acquisition_time.count())); } @@ -311,7 +318,7 @@ void chain_plugin_impl::start_write_processing() if (write_lock_held_duration > fc::milliseconds( write_lock_hold_time ) ) { wlog("Stopped processing write_queue before empty because we exceeded ${write_lock_hold_time}ms, " - "held lock for ${write_lock_held_duration}μs", + "held lock for ${write_lock_held_duration}μs", ("write_lock_hold_time", write_lock_hold_time) ("write_lock_held_duration", write_lock_held_duration.count())); break; @@ -322,13 +329,31 @@ void chain_plugin_impl::start_write_processing() { break; } + + last_popped_block_time = fc::time_point::now(); } }); } if( !is_syncing ) boost::this_thread::sleep_for( boost::chrono::milliseconds( 10 ) ); + + auto now = fc::time_point::now(); + if((now - last_popped_block_time) > block_wait_max_time) + { + if (now - last_msg_time > block_wait_max_time) + { + last_msg_time = now; + wlog("No P2P data (block/transaction) received in last ${t} seconds... peer_count=${peer_count}", ("t", block_wait_max_time.to_seconds())("peer_count",peer_count.load())); + /// To avoid log pollution + wlog("Checking for new P2P data once per ${t} seconds...", ("t", HIVE_BLOCK_INTERVAL)); + } + + boost::this_thread::sleep_for(boost::chrono::seconds(HIVE_BLOCK_INTERVAL)); + } } + + ilog("Write processing thread finished."); }); } @@ -337,12 +362,16 @@ void chain_plugin_impl::stop_write_processing() running = false; if( write_processor_thread ) + { + ilog("Waiting for write processing thread finish..."); write_processor_thread->join(); + ilog("Write processing thread stopped."); + } write_processor_thread.reset(); } -bool chain_plugin_impl::start_replay_processing( synchronization_type& on_sync ) +bool chain_plugin_impl::start_replay_processing() { bool replay_is_last_operation = replay_blockchain(); @@ -350,8 +379,10 @@ bool chain_plugin_impl::start_replay_processing( synchronization_type& on_sync ) { if( !appbase::app().is_interrupt_request() ) { + ilog("Generating artificial interrupt request..."); + /* - Triggering artifical signal. + Triggering artificial signal. Whole application should be closed in identical way, as if it was closed by user. This case occurs only when `exit-after-replay` switch is used. */ @@ -360,7 +391,7 @@ bool chain_plugin_impl::start_replay_processing( synchronization_type& on_sync ) } else { - //if `stop_replay_at` > 0 stay in API node context( without p2p connections ) + //if `stop_replay_at` > 0 stay in API node context( without synchronization ) if( stop_replay_at > 0 ) is_p2p_enabled = false; } @@ -409,23 +440,6 @@ void chain_plugin_impl::initial_settings() fc::variant database_config; -#ifdef ENABLE_MIRA - try - { - database_config = fc::json::from_file( database_cfg, fc::json::strict_parser ); - } - catch ( const std::exception& e ) - { - elog( "Error while parsing database configuration: ${e}", ("e", e.what()) ); - exit( EXIT_FAILURE ); - } - catch ( const fc::exception& e ) - { - elog( "Error while parsing database configuration: ${e}", ("e", e.what()) ); - exit( EXIT_FAILURE ); - } -#endif - db_open_args.data_dir = app().data_dir() / "blockchain"; db_open_args.shared_mem_dir = shared_memory_dir; db_open_args.initial_supply = HIVE_INIT_SUPPLY; @@ -433,7 +447,6 @@ void chain_plugin_impl::initial_settings() db_open_args.shared_file_size = shared_memory_size; db_open_args.shared_file_full_threshold = shared_file_full_threshold; db_open_args.shared_file_scale_rate = shared_file_scale_rate; - db_open_args.sps_remove_threshold = sps_remove_threshold; db_open_args.chainbase_flags = chainbase_flags; db_open_args.do_validate_invariants = validate_invariants; db_open_args.stop_replay_at = stop_replay_at; @@ -571,7 +584,7 @@ void chain_plugin_impl::process_snapshot() void chain_plugin_impl::work( synchronization_type& on_sync ) { - ilog( "Started on blockchain with ${n} blocks", ("n", db.head_block_num()) ); + ilog( "Started on blockchain with ${n} blocks, LIB: ${lb}", ("n", db.head_block_num())("lb", db.get_last_irreversible_block_num()) ); on_sync(); @@ -601,8 +614,7 @@ bfs::path chain_plugin::state_storage_dir() const void chain_plugin::set_program_options(options_description& cli, options_description& cfg) { cfg.add_options() - ("sps-remove-threshold", bpo::value()->default_value( 200 ), "Maximum numbers of proposals/votes which can be removed in the same cycle") - ("shared-file-dir", bpo::value()->default_value("blockchain"), + ("shared-file-dir", bpo::value()->default_value("blockchain"), // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall) "the location of the chain shared memory files (absolute path or relative to application data dir)") ("shared-file-size", bpo::value()->default_value("54G"), "Size of the shared memory file. Default: 54G. If running a full node, increase this value to 200G.") ("shared-file-full-threshold", bpo::value()->default_value(0), @@ -612,27 +624,19 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip ("checkpoint,c", bpo::value>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.") ("flush-state-interval", bpo::value(), "flush shared memory changes to disk every N blocks") -#ifdef ENABLE_MIRA - ("memory-replay-indices", bpo::value>()->multitoken()->composing(), "Specify which indices should be in memory during replay") -#endif ; cli.add_options() - ("sps-remove-threshold", bpo::value()->default_value( 200 ), "Maximum numbers of proposals/votes which can be removed in the same cycle") ("replay-blockchain", bpo::bool_switch()->default_value(false), "clear chain database and replay all blocks" ) ("force-open", bpo::bool_switch()->default_value(false), "force open the database, skipping the environment check" ) ("resync-blockchain", bpo::bool_switch()->default_value(false), "clear chain database and block log" ) ("stop-replay-at-block", bpo::value(), "Stop after reaching given block number") ("exit-after-replay", bpo::bool_switch()->default_value(false), "Exit after reaching given block number") - ("force-replay", bpo::bool_switch()->default_value(false), "Before replaying clean all old files") + ("force-replay", bpo::bool_switch()->default_value(false), "Before replaying clean all old files. If specifed, `--replay-blockchain` flag is implied") ("advanced-benchmark", "Make profiling for every plugin.") ("set-benchmark-interval", bpo::value(), "Print time and memory usage every given number of blocks") ("dump-memory-details", bpo::bool_switch()->default_value(false), "Dump database objects memory usage info. Use set-benchmark-interval to set dump interval.") ("check-locks", bpo::bool_switch()->default_value(false), "Check correctness of chainbase locking" ) ("validate-database-invariants", bpo::bool_switch()->default_value(false), "Validate all supply invariants check out" ) -#ifdef ENABLE_MIRA - ("database-cfg", bpo::value()->default_value("database.cfg"), "The database configuration file location") - ("memory-replay,m", bpo::bool_switch()->default_value(false), "Replay with state in memory instead of on disk") -#endif #ifdef IS_TEST_NET ("chain-id", bpo::value< std::string >()->default_value( HIVE_CHAIN_ID ), "chain ID to connect to") #endif @@ -659,15 +663,13 @@ void chain_plugin::plugin_initialize(const variables_map& options) { if( options.count( "shared-file-scale-rate" ) ) my->shared_file_scale_rate = options.at( "shared-file-scale-rate" ).as< uint16_t >(); - my->sps_remove_threshold = options.at( "sps-remove-threshold" ).as< uint16_t >(); - my->chainbase_flags |= options.at( "force-open" ).as< bool >() ? chainbase::skip_env_check : chainbase::skip_nothing; - my->replay = options.at( "replay-blockchain").as(); + my->force_replay = options.count( "force-replay" ) ? options.at( "force-replay" ).as() : false; + my->replay = options.at( "replay-blockchain").as() || my->force_replay; my->resync = options.at( "resync-blockchain").as(); my->stop_replay_at = options.count( "stop-replay-at-block" ) ? options.at( "stop-replay-at-block" ).as() : 0; my->exit_after_replay = options.count( "exit-after-replay" ) ? options.at( "exit-after-replay" ).as() : false; - my->force_replay = options.count( "force-replay" ) ? options.at( "force-replay" ).as() : false; my->benchmark_interval = options.count( "set-benchmark-interval" ) ? options.at( "set-benchmark-interval" ).as() : 0; my->check_locks = options.at( "check-locks" ).as< bool >(); @@ -695,29 +697,6 @@ void chain_plugin::plugin_initialize(const variables_map& options) { { my->statsd_on_replay = options.at( "statsd-record-on-replay" ).as< bool >(); } -#ifdef ENABLE_MIRA - my->database_cfg = options.at( "database-cfg" ).as< bfs::path >(); - - if( my->database_cfg.is_relative() ) - my->database_cfg = app().data_dir() / my->database_cfg; - - if( !bfs::exists( my->database_cfg ) ) - { - my->write_default_database_config( my->database_cfg ); - } - - my->replay_in_memory = options.at( "memory-replay" ).as< bool >(); - if ( options.count( "memory-replay-indices" ) ) - { - std::vector indices = options.at( "memory-replay-indices" ).as< vector< string > >(); - for ( auto& element : indices ) - { - std::vector< std::string > tmp; - boost::split( tmp, element, boost::is_any_of("\t ") ); - my->replay_memory_indices.insert( my->replay_memory_indices.end(), tmp.begin(), tmp.end() ); - } - } -#endif #ifdef IS_TEST_NET if( options.count( "chain-id" ) ) @@ -751,7 +730,7 @@ void chain_plugin::plugin_startup() if( my->replay ) { ilog("Replaying..."); - if( !my->start_replay_processing( on_sync ) ) + if( !my->start_replay_processing() ) { ilog("P2P enabling after replaying..."); my->work( on_sync ); @@ -766,7 +745,7 @@ void chain_plugin::plugin_startup() { ilog("Replaying..."); //Replaying is forced, because after snapshot loading, node should work in synchronization mode. - if( !my->start_replay_processing( on_sync ) ) + if( !my->start_replay_processing() ) { ilog("P2P enabling after replaying..."); my->work( on_sync ); @@ -802,6 +781,11 @@ void chain_plugin::report_state_options( const string& plugin_name, const fc::va my->plugin_state_opts( opts ); } +void chain_plugin::connection_count_changed(uint32_t peer_count) +{ + my->peer_count = peer_count; +} + bool chain_plugin::accept_block( const hive::chain::signed_block& block, bool currently_syncing, uint32_t skip ) { if (currently_syncing && block.block_num() % 10000 == 0) { @@ -904,7 +888,7 @@ void chain_plugin::register_block_generator( const std::string& plugin_name, std wlog( "Overriding a previously registered block generator by: ${registrant}", ("registrant", my->block_generator_registrant) ); my->block_generator_registrant = plugin_name; - my->block_generator = block_producer; + my->block_generator = std::move( block_producer ); } bool chain_plugin::is_p2p_enabled() const diff --git a/libraries/plugins/chain/include/hive/plugins/chain/chain_plugin.hpp b/libraries/plugins/chain/include/hive/plugins/chain/chain_plugin.hpp index 9572bad2e5..2d58912473 100644 --- a/libraries/plugins/chain/include/hive/plugins/chain/chain_plugin.hpp +++ b/libraries/plugins/chain/include/hive/plugins/chain/chain_plugin.hpp @@ -43,6 +43,7 @@ class chain_plugin : public plugin< chain_plugin > void report_state_options( const string& plugin_name, const fc::variant_object& opts ); + void connection_count_changed(uint32_t peer_count); bool accept_block( const hive::chain::signed_block& block, bool currently_syncing, uint32_t skip ); void accept_transaction( const hive::chain::signed_transaction& trx ); hive::chain::signed_block generate_block( diff --git a/libraries/plugins/chain/include/hive/plugins/chain/state_snapshot_provider.hpp b/libraries/plugins/chain/include/hive/plugins/chain/state_snapshot_provider.hpp index 597086a380..fd66c6cc0e 100644 --- a/libraries/plugins/chain/include/hive/plugins/chain/state_snapshot_provider.hpp +++ b/libraries/plugins/chain/include/hive/plugins/chain/state_snapshot_provider.hpp @@ -1,66 +1,66 @@ -#pragma once - -namespace appbase -{ -class abstract_plugin; -} /// namespace appbase - -namespace fc -{ -class path; -} /// namespace fc - -namespace hive { -namespace chain { -struct open_args; -} /// namespace chain - -namespace plugins { -namespace chain { - -using abstract_plugin = appbase::abstract_plugin; - -/** Purpose of this interface is provide state snapshot functionality to the chain_plugin even it is implemented in separate one. -*/ -class state_snapshot_provider -{ - public: - /** Allows to process explicit snapshot requests specified at application command line. - */ - virtual void process_explicit_snapshot_requests(const hive::chain::open_args& openArgs) = 0; - - protected: - virtual ~state_snapshot_provider() = default; -}; - -class snapshot_dump_helper -{ -public: - /// - /// Allows to store additional (external) data held in given plugin in the snapshot being produced atm. - /// - /// object representing given plugin, to dump external data for - /// specifies the directory where external data shall be written to - virtual void store_external_data_info(const abstract_plugin& plugin, const fc::path& storage_path) = 0; - -protected: - virtual ~snapshot_dump_helper() {} -}; - -class snapshot_load_helper -{ -public: - /// - /// Allows to ask snapshot provider for additional data (to be loaded) for given plugin. - /// - /// object representing given plugin, asking to load external data for - /// output parameter to be filled with the storage path of external data specific to given plugin - /// true if given plugin had saved external data to be load for. - virtual bool load_external_data_info(const abstract_plugin& plugin, fc::path* storage_path) = 0; - -protected: - virtual ~snapshot_load_helper() {} - -}; - +#pragma once + +namespace appbase +{ +class abstract_plugin; +} /// namespace appbase + +namespace fc +{ +class path; +} /// namespace fc + +namespace hive { +namespace chain { +struct open_args; +} /// namespace chain + +namespace plugins { +namespace chain { + +using abstract_plugin = appbase::abstract_plugin; + +/** Purpose of this interface is provide state snapshot functionality to the chain_plugin even it is implemented in separate one. +*/ +class state_snapshot_provider +{ + public: + /** Allows to process explicit snapshot requests specified at application command line. + */ + virtual void process_explicit_snapshot_requests(const hive::chain::open_args& openArgs) = 0; + + protected: + virtual ~state_snapshot_provider() = default; +}; + +class snapshot_dump_helper +{ +public: + /// + /// Allows to store additional (external) data held in given plugin in the snapshot being produced atm. + /// + /// object representing given plugin, to dump external data for + /// specifies the directory where external data shall be written to + virtual void store_external_data_info(const abstract_plugin& plugin, const fc::path& storage_path) = 0; + +protected: + virtual ~snapshot_dump_helper() {} +}; + +class snapshot_load_helper +{ +public: + /// + /// Allows to ask snapshot provider for additional data (to be loaded) for given plugin. + /// + /// object representing given plugin, asking to load external data for + /// output parameter to be filled with the storage path of external data specific to given plugin + /// true if given plugin had saved external data to be load for. + virtual bool load_external_data_info(const abstract_plugin& plugin, fc::path* storage_path) = 0; + +protected: + virtual ~snapshot_load_helper() {} + +}; + }}} \ No newline at end of file diff --git a/libraries/plugins/comment_cashout_logging/comment_cashout_logging_plugin.cpp b/libraries/plugins/comment_cashout_logging/comment_cashout_logging_plugin.cpp index 30b1905c28..0a87c64f03 100644 --- a/libraries/plugins/comment_cashout_logging/comment_cashout_logging_plugin.cpp +++ b/libraries/plugins/comment_cashout_logging/comment_cashout_logging_plugin.cpp @@ -15,7 +15,6 @@ using namespace hive::protocol; using chain::database; using chain::operation_notification; -using chain::operation_object; namespace detail { diff --git a/libraries/plugins/follow/follow_evaluators.cpp b/libraries/plugins/follow/follow_evaluators.cpp index 14d432d5ff..a51ebab7c6 100644 --- a/libraries/plugins/follow/follow_evaluators.cpp +++ b/libraries/plugins/follow/follow_evaluators.cpp @@ -170,9 +170,7 @@ void reblog_evaluator::do_apply( const reblog_operation& o ) const auto& idx = _db.get_index< follow_index >().indices().get< by_following_follower >(); auto itr = idx.find( o.account ); -#ifndef ENABLE_MIRA const auto& old_feed_idx = _db.get_index< feed_index >().indices().get< by_feed >(); -#endif performance_data pd; @@ -187,9 +185,7 @@ void reblog_evaluator::do_apply( const reblog_operation& o ) pd.init( o.account, _db.head_block_time(), c.get_id(), is_empty, is_empty ? 0 : feed_itr->account_feed_id ); uint32_t next_id = 0; -#ifndef ENABLE_MIRA perf.delete_old_objects< performance_data::t_creation_type::full_feed >( old_feed_idx, itr->follower, _plugin->max_feed_size, pd ); -#endif if( pd.s.creation ) { diff --git a/libraries/plugins/follow/follow_plugin.cpp b/libraries/plugins/follow/follow_plugin.cpp index e4d145aa22..3617378091 100644 --- a/libraries/plugins/follow/follow_plugin.cpp +++ b/libraries/plugins/follow/follow_plugin.cpp @@ -195,10 +195,7 @@ struct post_operation_visitor const auto& comment_idx = db.get_index< feed_index >().indices().get< by_comment >(); auto itr = idx.find( op.author ); -#ifndef ENABLE_MIRA const auto& old_feed_idx = db.get_index< feed_index >().indices().get< by_feed >(); -#endif - performance_data pd; @@ -212,10 +209,7 @@ struct post_operation_visitor bool is_empty = feed_itr == comment_idx.end(); pd.init( c.get_id(), is_empty ); - uint32_t next_id = 0; -#ifndef ENABLE_MIRA - next_id = perf.delete_old_objects< performance_data::t_creation_type::part_feed >( old_feed_idx, itr->follower, _plugin._self.max_feed_size, pd ); -#endif + uint32_t next_id = perf.delete_old_objects< performance_data::t_creation_type::part_feed >( old_feed_idx, itr->follower, _plugin._self.max_feed_size, pd ); if( pd.s.creation && is_empty ) { @@ -236,15 +230,10 @@ struct post_operation_visitor auto blog_itr = comment_blog_idx.find( boost::make_tuple( c.get_id(), op.author ) ); bool is_empty = blog_itr == comment_blog_idx.end(); -#ifndef ENABLE_MIRA const auto& old_blog_idx = db.get_index< blog_index >().indices().get< by_blog >(); -#endif pd.init( c.get_id(), is_empty ); - uint32_t next_id = 0; -#ifndef ENABLE_MIRA - next_id = perf.delete_old_objects< performance_data::t_creation_type::full_blog >( old_blog_idx, op.author, _plugin._self.max_feed_size, pd ); -#endif + uint32_t next_id = perf.delete_old_objects< performance_data::t_creation_type::full_blog >( old_blog_idx, op.author, _plugin._self.max_feed_size, pd ); if( pd.s.creation && is_empty ) { diff --git a/libraries/plugins/follow/inc_performance.cpp b/libraries/plugins/follow/inc_performance.cpp index ec685c750e..8ae4975af0 100644 --- a/libraries/plugins/follow/inc_performance.cpp +++ b/libraries/plugins/follow/inc_performance.cpp @@ -33,10 +33,8 @@ class performance_impl performance_impl( database& _db ); ~performance_impl(); -#ifndef ENABLE_MIRA template< performance_data::t_creation_type CreationType, typename Index > uint32_t delete_old_objects( Index& old_idx, const account_name_type& start_account, uint32_t max_size, performance_data& pd ) const; -#endif }; @@ -163,7 +161,6 @@ void performance_impl::remember_last( bool is_delayed, bool& init, Iterator& act } } -#ifndef ENABLE_MIRA template< performance_data::t_creation_type CreationType, typename Index > uint32_t performance_impl::delete_old_objects( Index& old_idx, const account_name_type& start_account, uint32_t max_size, performance_data& pd ) const { @@ -197,7 +194,6 @@ uint32_t performance_impl::delete_old_objects( Index& old_idx, const account_nam return next_id; } -#endif performance::performance( database& _db ) : my( new performance_impl( _db ) ) @@ -210,7 +206,6 @@ performance::~performance() } -#ifndef ENABLE_MIRA template< performance_data::t_creation_type CreationType, typename Index > uint32_t performance::delete_old_objects( Index& old_idx, const account_name_type& start_account, uint32_t max_size, performance_data& pd ) const { @@ -224,6 +219,5 @@ using t_blog = decltype( ((database*)nullptr)->get_index< blog_index >().indices template uint32_t performance::delete_old_objects< performance_data::t_creation_type::full_feed >( t_feed& old_idx, const account_name_type& start_account, uint32_t max_size, performance_data& pd ) const; template uint32_t performance::delete_old_objects< performance_data::t_creation_type::part_feed >( t_feed& old_idx, const account_name_type& start_account, uint32_t max_size, performance_data& pd ) const; template uint32_t performance::delete_old_objects< performance_data::t_creation_type::full_blog >( t_blog& old_idx, const account_name_type& start_account, uint32_t max_size, performance_data& pd ) const; -#endif } } } //hive::follow diff --git a/libraries/plugins/follow/include/hive/plugins/follow/inc_performance.hpp b/libraries/plugins/follow/include/hive/plugins/follow/inc_performance.hpp index a0e5ec2ca2..91423dedd1 100644 --- a/libraries/plugins/follow/include/hive/plugins/follow/inc_performance.hpp +++ b/libraries/plugins/follow/include/hive/plugins/follow/inc_performance.hpp @@ -21,14 +21,7 @@ class dumper static std::unique_ptr< dumper > self; - dumper() : -#if ENABLE_MIRA == 1 - f( "std_dumped_objects.txt" ) -#else - f( "bip_dumped_objects.txt" ) -#endif - { - } + dumper() : f( "bip_dumped_objects.txt" ) { } public: @@ -123,10 +116,8 @@ class performance performance( database& _db ); ~performance(); -#ifndef ENABLE_MIRA template< performance_data::t_creation_type CreationType, typename Index > uint32_t delete_old_objects( Index& old_idx, const account_name_type& start_account, uint32_t max_size, performance_data& pd ) const; -#endif template< typename T, typename T2 > static void dump( const char* message, const T& data, const T2& data2 ) diff --git a/libraries/plugins/json_rpc/include/hive/plugins/json_rpc/json_rpc_plugin.hpp b/libraries/plugins/json_rpc/include/hive/plugins/json_rpc/json_rpc_plugin.hpp index b1b9220e4e..bc7256492c 100644 --- a/libraries/plugins/json_rpc/include/hive/plugins/json_rpc/json_rpc_plugin.hpp +++ b/libraries/plugins/json_rpc/include/hive/plugins/json_rpc/json_rpc_plugin.hpp @@ -98,7 +98,9 @@ class json_rpc_plugin : public appbase::plugin< json_rpc_plugin > virtual void plugin_initialize( const variables_map& options ) override; virtual void plugin_startup() override; + virtual void plugin_pre_shutdown() override; virtual void plugin_shutdown() override; + virtual void plugin_finalize_startup() override; void add_api_method( const string& api_name, const string& method_name, const api_method& api, const api_method_signature& sig ); string call( const string& body ); diff --git a/libraries/plugins/json_rpc/json_rpc_plugin.cpp b/libraries/plugins/json_rpc/json_rpc_plugin.cpp index cc498938f9..e050ff11b1 100644 --- a/libraries/plugins/json_rpc/json_rpc_plugin.cpp +++ b/libraries/plugins/json_rpc/json_rpc_plugin.cpp @@ -24,7 +24,7 @@ namespace detail : code( 0 ) {} json_rpc_error( int32_t c, std::string m, fc::optional< fc::variant > d = fc::optional< fc::variant >() ) - : code( c ), message( m ), data( d ) {} + : code( c ), message( std::move( m ) ), data( d ) {} int32_t code; std::string message; @@ -129,13 +129,32 @@ namespace detail class json_rpc_plugin_impl { + + /* + Problem: + In general API plugins are attached to JSON plugin. Every attachment has to be done after initial actions. + If any methods are registered earlier, then it's a risk that calls to a node can be done before launching some initial actions in result undefined behaviour can occur f.e. segfault. + + Solution: + Collections `proxy_data` are used when gathering of API methods when an initialization of plugins is in progress. + When all plugins are initialized, gathered data is moved into `data` object. + */ + struct + { + map< string, api_description > _registered_apis; + vector< string > _methods; + map< string, map< string, api_method_signature > > _method_sigs; + } data, proxy_data; + public: json_rpc_plugin_impl(); ~json_rpc_plugin_impl(); void add_api_method( const string& api_name, const string& method_name, const api_method& api, const api_method_signature& sig ); + void plugin_finalize_startup(); + void plugin_pre_shutdown(); - api_method* find_api_method( std::string api, std::string method ); + api_method* find_api_method( const std::string& api, const std::string& method ); api_method* process_params( string method, const fc::variant_object& request, fc::variant& func_args, string* method_name ); void rpc_id( const fc::variant_object& request, json_rpc_response& response ); void rpc_jsonrpc( const fc::variant_object& request, json_rpc_response& response ); @@ -153,9 +172,6 @@ namespace detail (get_methods) (get_signature) ) - map< string, api_description > _registered_apis; - vector< string > _methods; - map< string, map< string, api_method_signature > > _method_sigs; std::unique_ptr< json_rpc_logger > _logger; }; @@ -164,12 +180,28 @@ namespace detail void json_rpc_plugin_impl::add_api_method( const string& api_name, const string& method_name, const api_method& api, const api_method_signature& sig ) { - _registered_apis[ api_name ][ method_name ] = api; - _method_sigs[ api_name ][ method_name ] = sig; + proxy_data._registered_apis[ api_name ][ method_name ] = api; + proxy_data._method_sigs[ api_name ][ method_name ] = sig; std::stringstream canonical_name; canonical_name << api_name << '.' << method_name; - _methods.push_back( canonical_name.str() ); + proxy_data._methods.push_back( canonical_name.str() ); + } + + void json_rpc_plugin_impl::plugin_finalize_startup() + { + std::sort( proxy_data._methods.begin(), proxy_data._methods.end() ); + + data._registered_apis = std::move( proxy_data._registered_apis ); + data._methods = std::move( proxy_data._methods ); + data._method_sigs = std::move( proxy_data._method_sigs ); + } + + void json_rpc_plugin_impl::plugin_pre_shutdown() + { + data._registered_apis.clear(); + data._methods.clear(); + data._method_sigs.clear(); } void json_rpc_plugin_impl::initialize() @@ -180,7 +212,7 @@ namespace detail get_methods_return json_rpc_plugin_impl::get_methods( const get_methods_args& args, bool lock ) { FC_UNUSED( lock ) - return _methods; + return data._methods; } get_signature_return json_rpc_plugin_impl::get_signature( const get_signature_args& args, bool lock ) @@ -190,8 +222,8 @@ namespace detail boost::split( v, args.method, boost::is_any_of( "." ) ); FC_ASSERT( v.size() == 2, "Invalid method name" ); - auto api_itr = _method_sigs.find( v[0] ); - FC_ASSERT( api_itr != _method_sigs.end(), "Method ${api}.${method} does not exist.", ("api", v[0])("method", v[1]) ); + auto api_itr = data._method_sigs.find( v[0] ); + FC_ASSERT( api_itr != data._method_sigs.end(), "Method ${api}.${method} does not exist.", ("api", v[0])("method", v[1]) ); auto method_itr = api_itr->second.find( v[1] ); FC_ASSERT( method_itr != api_itr->second.end(), "Method ${api}.${method} does not exist", ("api", v[0])("method", v[1]) ); @@ -199,11 +231,11 @@ namespace detail return method_itr->second; } - api_method* json_rpc_plugin_impl::find_api_method( std::string api, std::string method ) + api_method* json_rpc_plugin_impl::find_api_method( const std::string& api, const std::string& method ) { STATSD_START_TIMER( "jsonrpc", "overhead", "find_api_method", 1.0f ); - auto api_itr = _registered_apis.find( api ); - FC_ASSERT( api_itr != _registered_apis.end(), "Could not find API ${api}", ("api", api) ); + auto api_itr = data._registered_apis.find( api ); + FC_ASSERT( api_itr != data._registered_apis.end(), "Could not find API ${api}", ("api", api) ); auto method_itr = api_itr->second.find( method ); FC_ASSERT( method_itr != api_itr->second.end(), "Could not find method ${method}", ("method", method) ); @@ -430,13 +462,20 @@ void json_rpc_plugin::plugin_initialize( const variables_map& options ) } } -void json_rpc_plugin::plugin_startup() +void json_rpc_plugin::plugin_startup() {} + +void json_rpc_plugin::plugin_pre_shutdown() { - std::sort( my->_methods.begin(), my->_methods.end() ); + my->plugin_pre_shutdown(); } void json_rpc_plugin::plugin_shutdown() {} +void json_rpc_plugin::plugin_finalize_startup() +{ + my->plugin_finalize_startup(); +} + void json_rpc_plugin::add_api_method( const string& api_name, const string& method_name, const api_method& api, const api_method_signature& sig ) { my->add_api_method( api_name, method_name, api, sig ); diff --git a/libraries/plugins/p2p/include/hive/plugins/p2p/p2p_default_seeds.hpp b/libraries/plugins/p2p/include/hive/plugins/p2p/p2p_default_seeds.hpp index 31b763cadd..e8d65587e6 100644 --- a/libraries/plugins/p2p/include/hive/plugins/p2p/p2p_default_seeds.hpp +++ b/libraries/plugins/p2p/include/hive/plugins/p2p/p2p_default_seeds.hpp @@ -15,7 +15,7 @@ const std::vector< std::string > default_seeds = { "hiveseed-se.privex.io:2001", // privex "node.mahdiyari.info:2001", // mahdiyari "rpc.ausbit.dev:2001", // ausbitbank - "seed.chitty.me:2001", // chitty + "api.hive.blog:2001", // blocktrades "seed.hivekings.com:2001", // drakos "seed.liondani.com:2016", // liondani "seed.openhive.network:2001", // gtg diff --git a/libraries/plugins/p2p/include/hive/plugins/p2p/p2p_plugin.hpp b/libraries/plugins/p2p/include/hive/plugins/p2p/p2p_plugin.hpp index 750c1e8df9..2c51800866 100644 --- a/libraries/plugins/p2p/include/hive/plugins/p2p/p2p_plugin.hpp +++ b/libraries/plugins/p2p/include/hive/plugins/p2p/p2p_plugin.hpp @@ -2,13 +2,30 @@ #include #include +#include + #include +#include #define HIVE_P2P_PLUGIN_NAME "p2p" namespace hive { namespace plugins { namespace p2p { namespace bpo = boost::program_options; +struct api_peer_status // mirrors graphene::net::peer_status +{ + api_peer_status(uint32_t version, + fc::ip::endpoint host, + fc::variant_object info) : + version(version), host(host), info(info) + {} + api_peer_status(){} + + uint32_t version; + fc::ip::endpoint host; + fc::variant_object info; +}; + namespace detail { class p2p_plugin_impl; } class p2p_plugin : public appbase::plugin @@ -26,14 +43,22 @@ class p2p_plugin : public appbase::plugin virtual void plugin_initialize(const bpo::variables_map& options) override; virtual void plugin_startup() override; + virtual void plugin_pre_shutdown() override; virtual void plugin_shutdown() override; void broadcast_block( const hive::protocol::signed_block& block ); void broadcast_transaction( const hive::protocol::signed_transaction& tx ); void set_block_production( bool producing_blocks ); + fc::variant_object get_info(); + void add_node(const fc::ip::endpoint& endpoint); + void set_allowed_peers(const std::vector& allowed_peers); + std::vector< api_peer_status > get_connected_peers(); + private: std::unique_ptr< detail::p2p_plugin_impl > my; }; } } } // hive::plugins::p2p +FC_REFLECT( hive::plugins::p2p::api_peer_status, + (version)(host)(info) ) diff --git a/libraries/plugins/p2p/p2p_plugin.cpp b/libraries/plugins/p2p/p2p_plugin.cpp index 658ff6d155..74e8393976 100644 --- a/libraries/plugins/p2p/p2p_plugin.cpp +++ b/libraries/plugins/p2p/p2p_plugin.cpp @@ -1,834 +1,746 @@ -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -using std::string; -using std::vector; - -namespace hive { namespace plugins { namespace p2p { - -using appbase::app; - -using graphene::net::item_hash_t; -using graphene::net::item_id; -using graphene::net::message; -using graphene::net::block_message; -using graphene::net::trx_message; - -using hive::protocol::block_header; -using hive::protocol::signed_block_header; -using hive::protocol::signed_block; -using hive::protocol::block_id_type; - -namespace detail { - -// This exists in p2p_plugin and http_plugin. It should be added to fc. -std::vector resolve_string_to_ip_endpoints( const std::string& endpoint_string ) -{ - try - { - string::size_type colon_pos = endpoint_string.find( ':' ); - if( colon_pos == std::string::npos ) - FC_THROW( "Missing required port number in endpoint string \"${endpoint_string}\"", - ("endpoint_string", endpoint_string) ); - - std::string port_string = endpoint_string.substr( colon_pos + 1 ); - - try - { - uint16_t port = boost::lexical_cast< uint16_t >( port_string ); - std::string hostname = endpoint_string.substr( 0, colon_pos ); - std::vector< fc::ip::endpoint > endpoints = fc::resolve( hostname, port ); - - if( endpoints.empty() ) - FC_THROW_EXCEPTION( fc::unknown_host_exception, "The host name can not be resolved: ${hostname}", ("hostname", hostname) ); - - return endpoints; - } - catch( const boost::bad_lexical_cast& ) - { - FC_THROW("Bad port: ${port}", ("port", port_string) ); - } - } - FC_CAPTURE_AND_RETHROW( (endpoint_string) ) -} - -class p2p_plugin_impl : public graphene::net::node_delegate -{ -public: - - p2p_plugin_impl( plugins::chain::chain_plugin& c ) - : running(true), activeHandleBlock(false), activeHandleTx(false), chain( c ) - { - handleBlockFinished.second = std::shared_future(handleBlockFinished.first.get_future()); - handleTxFinished.second = std::shared_future(handleTxFinished.first.get_future()); - } - - virtual ~p2p_plugin_impl() - { - finish( handleBlockFinished ); - finish( handleTxFinished ); - } - - bool is_included_block(const block_id_type& block_id); - virtual hive::protocol::chain_id_type get_old_chain_id() const override; - virtual hive::protocol::chain_id_type get_new_chain_id() const override; - virtual hive::protocol::chain_id_type get_chain_id() const override; - - // node_delegate interface - virtual bool has_item( const graphene::net::item_id& ) override; - virtual bool handle_block( const graphene::net::block_message&, bool, std::vector& ) override; - virtual void handle_transaction( const graphene::net::trx_message& ) override; - virtual void handle_message( const graphene::net::message& ) override; - virtual std::vector< graphene::net::item_hash_t > get_block_ids( const std::vector< graphene::net::item_hash_t >&, uint32_t&, uint32_t ) override; - virtual graphene::net::message get_item( const graphene::net::item_id& ) override; - virtual std::vector< graphene::net::item_hash_t > get_blockchain_synopsis( const graphene::net::item_hash_t&, uint32_t ) override; - virtual void sync_status( uint32_t, uint32_t ) override; - virtual void connection_count_changed( uint32_t ) override; - virtual uint32_t get_block_number( const graphene::net::item_hash_t& ) override; - virtual fc::time_point_sec get_block_time(const graphene::net::item_hash_t& ) override; - virtual fc::time_point_sec get_blockchain_now() override; - virtual graphene::net::item_hash_t get_head_block_id() const override; - virtual uint32_t estimate_last_known_fork_from_git_revision_timestamp( uint32_t ) const override; - virtual void error_encountered( const std::string& message, const fc::oexception& error ) override; - - fc::optional endpoint; - vector seeds; - string user_agent; - fc::mutable_variant_object config; - uint32_t max_connections = 0; - bool force_validate = false; - bool block_producer = false; - std::atomic_bool running; - std::atomic_bool activeHandleBlock; - std::atomic_bool activeHandleTx; - typedef std::pair, std::shared_future> handler_state; - - handler_state handleBlockFinished; - handler_state handleTxFinished; - - std::unique_ptr node; - - plugins::chain::chain_plugin& chain; - - fc::thread p2p_thread; - -private: - class shutdown_helper final - { - public: - shutdown_helper(p2p_plugin_impl& impl, std::atomic_bool& activityFlag, - handler_state& barrier) : - _impl(impl), _barrier(barrier), _activityFlag(activityFlag) - { - _activityFlag.store(true); - } - ~shutdown_helper() - { - _activityFlag.store(false); - if(_impl.running.load() == false && _barrier.second.valid() == false) - { - ilog("Sending notification to shutdown barrier."); - _barrier.first.set_value(); - } - } - - private: - p2p_plugin_impl& _impl; - handler_state& _barrier; - std::atomic_bool& _activityFlag; - }; - - void finish( handler_state& handler ); - -}; - -void p2p_plugin_impl::finish( handler_state& handler ) -{ - if( handler.second.valid() ) - { - std::future_status res = handler.second.wait_for( std::chrono::milliseconds( 100 ) ); - - if( res != std::future_status::ready ) - { - try - { - handler.first.set_value(); - } - catch( const std::future_error& e ) - { - ilog("Future error exception when P2P plugin is closing. ( Code: ${c} )( Message: ${m} )", ( "c", e.code().value() )( "m", e.what() ) ); - return; - } - catch(...) - { - ilog("Unknown exception when P2P plugin is closing."); - return; - } - } - - uint32_t cnt = 0; - do - { - res = handler.second.wait_for( std::chrono::milliseconds( 100 ) ); - FC_ASSERT( ++cnt <= 10, "Closing the P2P plugin is terminated" ); - }while( res != std::future_status::ready ); - } -} - -////////////////////////////// Begin node_delegate Implementation ////////////////////////////// -bool p2p_plugin_impl::has_item( const graphene::net::item_id& id ) -{ - return chain.db().with_read_lock( [&]() - { - try - { - if( id.item_type == graphene::net::block_message_type ) - return chain.db().is_known_block(id.item_hash); - else - return chain.db().is_known_transaction(id.item_hash); - } - FC_CAPTURE_LOG_AND_RETHROW( (id) ) - }); -} - -bool p2p_plugin_impl::handle_block( const graphene::net::block_message& blk_msg, bool sync_mode, std::vector& ) -{ try { - if( running.load() ) - { - shutdown_helper helper(*this, activeHandleBlock, handleBlockFinished); - - uint32_t head_block_num; - chain.db().with_read_lock( [&]() - { - head_block_num = chain.db().head_block_num(); - }); - if (sync_mode) - fc_ilog(fc::logger::get("sync"), - "chain pushing sync block #${block_num} ${block_hash}, head is ${head}", - ("block_num", blk_msg.block.block_num()) - ("block_hash", blk_msg.block_id) - ("head", head_block_num)); - else - fc_ilog(fc::logger::get("sync"), - "chain pushing block #${block_num} ${block_hash}, head is ${head}", - ("block_num", blk_msg.block.block_num()) - ("block_hash", blk_msg.block_id) - ("head", head_block_num)); - - try { - // TODO: in the case where this block is valid but on a fork that's too old for us to switch to, - // you can help the network code out by throwing a block_older_than_undo_history exception. - // when the net code sees that, it will stop trying to push blocks from that chain, but - // leave that peer connected so that they can get sync blocks from us - bool result = chain.accept_block( blk_msg.block, sync_mode, ( block_producer | force_validate ) ? chain::database::skip_nothing : chain::database::skip_transaction_signatures ); - - if( !sync_mode ) - { - fc::microseconds offset = fc::time_point::now() - blk_msg.block.timestamp; - STATSD_TIMER( "p2p", "offset", "block_arrival", offset, 1.0f ) - ilog( "Got ${t} transactions on block ${b} by ${w} -- Block Time Offset: ${l} ms", - ("t", blk_msg.block.transactions.size()) - ("b", blk_msg.block.block_num()) - ("w", blk_msg.block.witness) - ("l", offset.count() / 1000) ); - } - - return result; - } catch ( const chain::unlinkable_block_exception& e ) { - // translate to a graphene::net exception - fc_elog(fc::logger::get("sync"), - "Error when pushing block, current head block is ${head}:\n${e}", - ("e", e.to_detail_string()) - ("head", head_block_num)); - elog("Error when pushing block:\n${e}", ("e", e.to_detail_string())); - FC_THROW_EXCEPTION(graphene::net::unlinkable_block_exception, "Error when pushing block:\n${e}", ("e", e.to_detail_string())); - } catch( const fc::exception& e ) { - fc_elog(fc::logger::get("sync"), - "Error when pushing block, current head block is ${head}:\n${e}", - ("e", e.to_detail_string()) - ("head", head_block_num)); - elog("Error when pushing block:\n${e}", ("e", e.to_detail_string())); - if (e.code() == 4080000) { - elog("Rethrowing as graphene::net exception"); - FC_THROW_EXCEPTION(graphene::net::unlinkable_block_exception, "Error when pushing block:\n${e}", ("e", e.to_detail_string())); - } else { - throw; - } - } - } - else - { - ilog("Block ignored due to started p2p_plugin shutdown"); - if(handleBlockFinished.second.valid() == false) - handleBlockFinished.first.set_value(); - FC_THROW("Preventing further processing of ignored block..."); - } - return false; -} FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) } - -void p2p_plugin_impl::handle_transaction( const graphene::net::trx_message& trx_msg ) -{ - if(running.load()) - { - try - { - shutdown_helper helper(*this, activeHandleTx, handleTxFinished); - - chain.accept_transaction( trx_msg.trx ); - - } FC_CAPTURE_AND_RETHROW( (trx_msg) ) - } - else - { - ilog("Transaction ignored due to started p2p_plugin shutdown"); - if(handleTxFinished.second.valid() == false) - handleTxFinished.first.set_value(); - - FC_THROW("Preventing further processing of ignored transaction..."); - } -} - -void p2p_plugin_impl::handle_message( const graphene::net::message& message_to_process ) -{ - // not a transaction, not a block - FC_THROW( "Invalid Message Type" ); -} - -std::vector< graphene::net::item_hash_t > p2p_plugin_impl::get_block_ids( const std::vector< graphene::net::item_hash_t >& blockchain_synopsis, uint32_t& remaining_item_count, uint32_t limit ) -{ try { - return chain.db().with_read_lock( [&]() - { - vector result; - remaining_item_count = 0; - if( chain.db().head_block_num() == 0 ) - return result; - - result.reserve( limit ); - block_id_type last_known_block_id; - - if( blockchain_synopsis.empty() - || ( blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type() ) ) - { - // peer has sent us an empty synopsis meaning they have no blocks. - // A bug in old versions would cause them to send a synopsis containing block 000000000 - // when they had an empty blockchain, so pretend they sent the right thing here. - // do nothing, leave last_known_block_id set to zero - } - else - { - bool found_a_block_in_synopsis = false; - - for( const item_hash_t& block_id_in_synopsis : boost::adaptors::reverse(blockchain_synopsis) ) - { - if (block_id_in_synopsis == block_id_type() || - (chain.db().is_known_block(block_id_in_synopsis) && is_included_block(block_id_in_synopsis))) - { - last_known_block_id = block_id_in_synopsis; - found_a_block_in_synopsis = true; - break; - } - } - - if (!found_a_block_in_synopsis) - FC_THROW_EXCEPTION(graphene::net::peer_is_on_an_unreachable_fork, "Unable to provide a list of blocks starting at any of the blocks in peer's synopsis"); - } - - for( uint32_t num = block_header::num_from_id(last_known_block_id); - num <= chain.db().head_block_num() && result.size() < limit; - ++num ) - { - if( num > 0 ) - result.push_back(chain.db().get_block_id_for_num(num)); - } - - if( !result.empty() && block_header::num_from_id(result.back()) < chain.db().head_block_num() ) - remaining_item_count = chain.db().head_block_num() - block_header::num_from_id(result.back()); - - return result; - }); -} FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) } - -graphene::net::message p2p_plugin_impl::get_item( const graphene::net::item_id& id ) -{ try { - if( id.item_type == graphene::net::block_message_type ) - { - return chain.db().with_read_lock( [&]() - { - auto opt_block = chain.db().fetch_block_by_id(id.item_hash); - if( !opt_block ) - elog("Couldn't find block ${id} -- corresponding ID in our chain is ${id2}", - ("id", id.item_hash)("id2", chain.db().get_block_id_for_num(block_header::num_from_id(id.item_hash)))); - FC_ASSERT( opt_block.valid() ); - // ilog("Serving up block #${num}", ("num", opt_block->block_num())); - return block_message(std::move(*opt_block)); - }); - } - return chain.db().with_read_lock( [&]() - { - return trx_message( chain.db().get_recent_transaction( id.item_hash ) ); - }); -} FC_CAPTURE_AND_RETHROW( (id) ) } - -hive::protocol::chain_id_type p2p_plugin_impl::get_old_chain_id() const -{ - return STEEM_CHAIN_ID; -} - -hive::protocol::chain_id_type p2p_plugin_impl::get_new_chain_id() const -{ - return HIVE_CHAIN_ID; -} - -hive::protocol::chain_id_type p2p_plugin_impl::get_chain_id() const -{ - return chain.db().get_chain_id(); -} - -std::vector< graphene::net::item_hash_t > p2p_plugin_impl::get_blockchain_synopsis( const graphene::net::item_hash_t& reference_point, uint32_t number_of_blocks_after_reference_point ) -{ - try { - std::vector synopsis; - chain.db().with_read_lock( [&]() - { - synopsis.reserve(30); - uint32_t high_block_num; - uint32_t non_fork_high_block_num; - uint32_t low_block_num = chain.db().last_non_undoable_block_num(); - std::vector fork_history; - - if (reference_point != item_hash_t()) - { - // the node is asking for a summary of the block chain up to a specified - // block, which may or may not be on a fork - // for now, assume it's not on a fork - if (is_included_block(reference_point)) - { - // reference_point is a block we know about and is on the main chain - uint32_t reference_point_block_num = block_header::num_from_id(reference_point); - assert(reference_point_block_num > 0); - high_block_num = reference_point_block_num; - non_fork_high_block_num = high_block_num; - - if (reference_point_block_num < low_block_num) - { - // we're on the same fork (at least as far as reference_point) but we've passed - // reference point and could no longer undo that far if we diverged after that - // block. This should probably only happen due to a race condition where - // the network thread calls this function, and then immediately pushes a bunch of blocks, - // then the main thread finally processes this function. - // with the current framework, there's not much we can do to tell the network - // thread what our current head block is, so we'll just pretend that - // our head is actually the reference point. - // this *may* enable us to fetch blocks that we're unable to push, but that should - // be a rare case (and correctly handled) - low_block_num = reference_point_block_num; - } - } - else - { - // block is a block we know about, but it is on a fork - try - { - fork_history = chain.db().get_block_ids_on_fork(reference_point); - // returns a vector where the last element is the common ancestor with the preferred chain, - // and the first element is the reference point you passed in - assert(fork_history.size() >= 2); - - if( fork_history.front() != reference_point ) - { - edump( (fork_history)(reference_point) ); - assert(fork_history.front() == reference_point); - } - block_id_type last_non_fork_block = fork_history.back(); - fork_history.pop_back(); // remove the common ancestor - boost::reverse(fork_history); - - if (last_non_fork_block == block_id_type()) // if the fork goes all the way back to genesis (does graphene's fork db allow this?) - non_fork_high_block_num = 0; - else - non_fork_high_block_num = block_header::num_from_id(last_non_fork_block); - - high_block_num = non_fork_high_block_num + fork_history.size(); - assert(high_block_num == block_header::num_from_id(fork_history.back())); - } - catch (const fc::exception& e) - { - // unable to get fork history for some reason. maybe not linked? - // we can't return a synopsis of its chain - elog("Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}", ("hash", reference_point)("exception", e)); - throw; - } - if (non_fork_high_block_num < low_block_num) - { - wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago " - "(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})", - ("low_block_num", low_block_num) - ("non_fork_high_block_num", non_fork_high_block_num)); - FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to"); - } - } - } - else - { - // no reference point specified, summarize the whole block chain - high_block_num = chain.db().head_block_num(); - non_fork_high_block_num = high_block_num; - if (high_block_num == 0) - return; // we have no blocks - } - - if( low_block_num == 0) - low_block_num = 1; - - // at this point: - // low_block_num is the block before the first block we can undo, - // non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num) - // high_block_num is the block number of the reference block, or the end of the chain if no reference provided - - // true_high_block_num is the ending block number after the network code appends any item ids it - // knows about that we don't - uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point; - do - { - // for each block in the synopsis, figure out where to pull the block id from. - // if it's <= non_fork_high_block_num, we grab it from the main blockchain; - // if it's not, we pull it from the fork history - if( low_block_num <= non_fork_high_block_num ) - synopsis.push_back(chain.db().get_block_id_for_num(low_block_num)); - else - synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]); - low_block_num += (true_high_block_num - low_block_num + 2) / 2; - } - while( low_block_num <= high_block_num ); - - //idump((synopsis)); - return; - }); - - return synopsis; -} FC_LOG_AND_RETHROW() } - -void p2p_plugin_impl::sync_status( uint32_t item_type, uint32_t item_count ) -{ - // any status reports to GUI go here -} - -void p2p_plugin_impl::connection_count_changed( uint32_t c ) -{ - // any status reports to GUI go here -} - -uint32_t p2p_plugin_impl::get_block_number( const graphene::net::item_hash_t& block_id ) -{ - try { - return block_header::num_from_id(block_id); -} FC_CAPTURE_AND_RETHROW( (block_id) ) } - -fc::time_point_sec p2p_plugin_impl::get_block_time( const graphene::net::item_hash_t& block_id ) -{ - try - { - return chain.db().with_read_lock( [&]() - { - auto opt_block = chain.db().fetch_block_by_id( block_id ); - if( opt_block.valid() ) return opt_block->timestamp; - return fc::time_point_sec::min(); - }); - } FC_CAPTURE_AND_RETHROW( (block_id) ) -} - -graphene::net::item_hash_t p2p_plugin_impl::get_head_block_id() const -{ try { - return chain.db().with_read_lock( [&]() - { - return chain.db().head_block_id(); - }); -} FC_CAPTURE_AND_RETHROW() } - -uint32_t p2p_plugin_impl::estimate_last_known_fork_from_git_revision_timestamp(uint32_t) const -{ - return 0; // there are no forks in graphene -} - -void p2p_plugin_impl::error_encountered( const string& message, const fc::oexception& error ) -{ - // notify GUI or something cool -} - -fc::time_point_sec p2p_plugin_impl::get_blockchain_now() -{ try { - return fc::time_point::now(); -} FC_CAPTURE_AND_RETHROW() } - -bool p2p_plugin_impl::is_included_block(const block_id_type& block_id) -{ try { - return chain.db().with_read_lock( [&]() - { - uint32_t block_num = block_header::num_from_id(block_id); - block_id_type block_id_in_preferred_chain = chain.db().get_block_id_for_num(block_num); - return block_id == block_id_in_preferred_chain; - }); -} FC_CAPTURE_AND_RETHROW() } - -////////////////////////////// End node_delegate Implementation ////////////////////////////// - -} // detail - -p2p_plugin::p2p_plugin() -{ -} - -p2p_plugin::~p2p_plugin() {} - -void p2p_plugin::set_program_options( bpo::options_description& cli, bpo::options_description& cfg) -{ - std::stringstream seed_ss; - for( auto& s : default_seeds ) - { - seed_ss << s << ' '; - } - - cfg.add_options() - ("p2p-endpoint", bpo::value()->implicit_value("127.0.0.1:9876"), "The local IP address and port to listen for incoming connections.") - ("p2p-max-connections", bpo::value(), "Maxmimum number of incoming connections on P2P endpoint.") - ("seed-node", bpo::value>()->composing(), "The IP address and port of a remote peer to sync with. Deprecated in favor of p2p-seed-node.") - ("p2p-seed-node", bpo::value>()->composing()->default_value( default_seeds, seed_ss.str() ), "The IP address and port of a remote peer to sync with.") - ("p2p-parameters", bpo::value(), ("P2P network parameters. (Default: " + fc::json::to_string(graphene::net::node_configuration()) + " )").c_str() ) - ; - cli.add_options() - ("force-validate", bpo::bool_switch()->default_value(false), "Force validation of all transactions. Deprecated in favor of p2p-force-validate" ) - ("p2p-force-validate", bpo::bool_switch()->default_value(false), "Force validation of all transactions." ) - ; -} - -void p2p_plugin::plugin_initialize(const boost::program_options::variables_map& options) -{ - my = std::make_unique< detail::p2p_plugin_impl >( appbase::app().get_plugin< plugins::chain::chain_plugin >() ); - - if( options.count( "p2p-endpoint" ) ) - my->endpoint = fc::ip::endpoint::from_string( options.at( "p2p-endpoint" ).as< string >() ); - - my->user_agent = "Hive Reference Implementation"; - - if( options.count( "p2p-max-connections" ) ) - my->max_connections = options.at( "p2p-max-connections" ).as< uint32_t >(); - - if( options.count( "seed-node" ) || options.count( "p2p-seed-node" ) ) - { - vector< string > seeds; - if( options.count( "seed-node" ) ) - { - wlog( "Option seed-node is deprecated in favor of p2p-seed-node" ); - auto s = options.at("seed-node").as>(); - for( auto& arg : s ) - { - vector< string > addresses; - boost::split( addresses, arg, boost::is_any_of( " \t" ) ); - seeds.insert( seeds.end(), addresses.begin(), addresses.end() ); - } - } - - if( options.count( "p2p-seed-node" ) ) - { - auto s = options.at("p2p-seed-node").as>(); - for( auto& arg : s ) - { - vector< string > addresses; - boost::split( addresses, arg, boost::is_any_of( " \t" ) ); - seeds.insert( seeds.end(), addresses.begin(), addresses.end() ); - } - } - - for( const string& endpoint_string : seeds ) - { - try - { - std::vector endpoints = detail::resolve_string_to_ip_endpoints(endpoint_string); - my->seeds.insert( my->seeds.end(), endpoints.begin(), endpoints.end() ); - } - catch( const fc::exception& e ) - { - wlog( "caught exception ${e} while adding seed node ${endpoint}", - ("e", e.to_detail_string())("endpoint", endpoint_string) ); - } - } - } - - my->force_validate = options.at( "p2p-force-validate" ).as< bool >(); - - if( !my->force_validate && options.at( "force-validate" ).as< bool >() ) - { - wlog( "Option force-validate is deprecated in favor of p2p-force-validate" ); - my->force_validate = true; - } - - if( options.count("p2p-parameters") ) - { - fc::variant var = fc::json::from_string( options.at("p2p-parameters").as(), fc::json::strict_parser ); - my->config = var.get_object(); - } -} - -void p2p_plugin::plugin_startup() -{ - if( !my->chain.is_p2p_enabled() ) - return; - - my->p2p_thread.async( [this] - { - my->node.reset(new graphene::net::node(my->user_agent)); - my->node->load_configuration(app().data_dir() / "p2p"); - my->node->set_node_delegate( &(*my) ); - - if( my->endpoint ) - { - ilog("Configuring P2P to listen at ${ep}", ("ep", my->endpoint)); - my->node->listen_on_endpoint(*my->endpoint, true); - } - - for( const auto& seed : my->seeds ) - { - try - { - ilog("P2P adding seed node ${s}", ("s", seed)); - my->node->add_node(seed); - my->node->connect_to_endpoint(seed); - } - catch( graphene::net::already_connected_to_requested_peer& ) - { - wlog( "Already connected to seed node ${s}. Is it specified twice in config?", ("s", seed) ); - } - } - - if( my->max_connections ) - { - if( my->config.find( "maximum_number_of_connections" ) != my->config.end() ) - ilog( "Overriding advanded_node_parameters[ \"maximum_number_of_connections\" ] with ${cons}", ("cons", my->max_connections) ); - - my->config.set( "maximum_number_of_connections", fc::variant( my->max_connections ) ); - } - - my->node->set_advanced_node_parameters( my->config ); - my->node->listen_to_p2p_network(); - my->node->connect_to_p2p_network(); - block_id_type block_id; - my->chain.db().with_read_lock( [&]() - { - block_id = my->chain.db().head_block_id(); - }); - my->node->sync_from(graphene::net::item_id(graphene::net::block_message_type, block_id), std::vector()); - ilog("P2P node listening at ${ep}", ("ep", my->node->get_actual_listening_endpoint())); - }).wait(); - ilog( "P2P Plugin started" ); -} - -const char* fStatus(std::future_status s) -{ - switch(s) - { - case std::future_status::ready: - return "ready"; - case std::future_status::deferred: - return "deferred"; - case std::future_status::timeout: - return "timeout"; - default: - return "unknown"; - } -} - -void p2p_plugin::plugin_shutdown() { - - if( !my->chain.is_p2p_enabled() ) - return; - - ilog("Shutting down P2P Plugin"); - my->running.store(false); - - ilog("P2P Plugin: checking handle_block and handle_transaction activity"); - std::future_status bfState, tfState; - do - { - if(my->activeHandleBlock.load()) - { - bfState = my->handleBlockFinished.second.wait_for(std::chrono::milliseconds(100)); - if(bfState != std::future_status::ready) - { - ilog("waiting for handle_block finish: ${s}, future status: ${fs}", - ("s", fStatus(bfState)) - ("fs", std::to_string(my->handleBlockFinished.second.valid())) - ); - } - } - else - { - bfState = std::future_status::ready; - } - - if(my->activeHandleTx.load()) - { - tfState = my->handleTxFinished.second.wait_for(std::chrono::milliseconds(100)); - if(tfState != std::future_status::ready) - { - ilog("waiting for handle_transaction finish: ${s}, future status: ${fs}", - ("s", fStatus(tfState)) - ("fs", std::to_string(my->handleTxFinished.second.valid())) - ); - } - } - else - { - tfState = std::future_status::ready; - } - } - while(bfState != std::future_status::ready && tfState != std::future_status::ready); - - ilog("P2P Plugin: checking handle_block and handle_transaction activity"); - my->node->close(); - fc::promise::ptr quitDone(new fc::promise("P2P thread quit")); - my->p2p_thread.quit(quitDone.get()); - ilog("Waiting for p2p_thread quit"); - quitDone->wait(); - ilog("p2p_thread quit done"); - my->node.reset(); -} - -void p2p_plugin::broadcast_block( const hive::protocol::signed_block& block ) -{ - ulog("Broadcasting block #${n}", ("n", block.block_num())); - my->node->broadcast( graphene::net::block_message( block ) ); -} - -void p2p_plugin::broadcast_transaction( const hive::protocol::signed_transaction& tx ) -{ - ulog("Broadcasting tx #${n}", ("id", tx.id())); - my->node->broadcast( graphene::net::trx_message( tx ) ); -} - -void p2p_plugin::set_block_production( bool producing_blocks ) -{ - my->block_producer = producing_blocks; -} - -} } } // namespace hive::plugins::p2p +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +using std::string; +using std::vector; + +#define HIVE_P2P_NUMBER_THREAD_SENSITIVE_ACTIONS 2 +#define HIVE_P2P_BLOCK_HANDLER 0 +#define HIVE_P2P_TRANSACTION_HANDLER 1 + +namespace hive { namespace plugins { namespace p2p { + +using appbase::app; + +using graphene::net::item_hash_t; +using graphene::net::item_id; +using graphene::net::message; +using graphene::net::block_message; +using graphene::net::trx_message; + +using hive::protocol::block_header; +using hive::protocol::signed_block; +using hive::protocol::block_id_type; + +namespace detail { + +// This exists in p2p_plugin and http_plugin. It should be added to fc. +std::vector resolve_string_to_ip_endpoints( const std::string& endpoint_string ) +{ + try + { + string::size_type colon_pos = endpoint_string.find( ':' ); + if( colon_pos == std::string::npos ) + FC_THROW( "Missing required port number in endpoint string \"${endpoint_string}\"", + ("endpoint_string", endpoint_string) ); + + std::string port_string = endpoint_string.substr( colon_pos + 1 ); + + try + { + uint16_t port = boost::lexical_cast< uint16_t >( port_string ); + std::string hostname = endpoint_string.substr( 0, colon_pos ); + std::vector< fc::ip::endpoint > endpoints = fc::resolve( hostname, port ); + + if( endpoints.empty() ) + FC_THROW_EXCEPTION( fc::unknown_host_exception, "The host name can not be resolved: ${hostname}", ("hostname", hostname) ); + + return endpoints; + } + catch( const boost::bad_lexical_cast& ) + { + FC_THROW("Bad port: ${port}", ("port", port_string) ); + } + } + FC_CAPTURE_AND_RETHROW( (endpoint_string) ) +} + +class p2p_plugin_impl : public graphene::net::node_delegate +{ +public: + + p2p_plugin_impl( plugins::chain::chain_plugin& c ) + : shutdown_helper( "P2P plugin", HIVE_P2P_NUMBER_THREAD_SENSITIVE_ACTIONS ), chain( c ) + { + } + virtual ~p2p_plugin_impl() + { + shutdown_helper.prepare_shutdown(); + shutdown_helper.wait(); + } + + bool is_included_block(const block_id_type& block_id); + virtual hive::protocol::chain_id_type get_old_chain_id() const override; + virtual hive::protocol::chain_id_type get_new_chain_id() const override; + virtual hive::protocol::chain_id_type get_chain_id() const override; + + // node_delegate interface + virtual bool has_item( const graphene::net::item_id& ) override; + virtual bool handle_block( const graphene::net::block_message&, bool, std::vector& ) override; + virtual void handle_transaction( const graphene::net::trx_message& ) override; + virtual void handle_message( const graphene::net::message& ) override; + virtual std::vector< graphene::net::item_hash_t > get_block_ids( const std::vector< graphene::net::item_hash_t >&, uint32_t&, uint32_t ) override; + virtual graphene::net::message get_item( const graphene::net::item_id& ) override; + virtual std::vector< graphene::net::item_hash_t > get_blockchain_synopsis( const graphene::net::item_hash_t&, uint32_t ) override; + virtual void sync_status( uint32_t, uint32_t ) override; + virtual void connection_count_changed( uint32_t ) override; + virtual uint32_t get_block_number( const graphene::net::item_hash_t& ) override; + virtual fc::time_point_sec get_block_time(const graphene::net::item_hash_t& ) override; + virtual fc::time_point_sec get_blockchain_now() override; + virtual graphene::net::item_hash_t get_head_block_id() const override; + virtual uint32_t estimate_last_known_fork_from_git_revision_timestamp( uint32_t ) const override; + virtual void error_encountered( const std::string& message, const fc::oexception& error ) override; + + fc::optional endpoint; + vector seeds; + string user_agent; + fc::mutable_variant_object config; + uint32_t max_connections = 0; + bool force_validate = false; + bool block_producer = false; + + shutdown_mgr shutdown_helper; + + std::unique_ptr node; + + plugins::chain::chain_plugin& chain; + + fc::thread p2p_thread; +}; + +////////////////////////////// Begin node_delegate Implementation ////////////////////////////// +bool p2p_plugin_impl::has_item( const graphene::net::item_id& id ) +{ + return chain.db().with_read_lock( [&]() + { + try + { + if( id.item_type == graphene::net::block_message_type ) + return chain.db().is_known_block(id.item_hash); + else + return chain.db().is_known_transaction(id.item_hash); + } + FC_CAPTURE_LOG_AND_RETHROW( (id) ) + }); +} + +bool p2p_plugin_impl::handle_block( const graphene::net::block_message& blk_msg, bool sync_mode, std::vector& ) +{ try { + if( shutdown_helper.get_running().load() ) + { + action_catcher ac( shutdown_helper.get_running(), shutdown_helper.get_state( HIVE_P2P_BLOCK_HANDLER ) ); + + uint32_t head_block_num; + chain.db().with_read_lock( [&]() + { + head_block_num = chain.db().head_block_num(); + }); + if (sync_mode) + fc_ilog(fc::logger::get("sync"), + "chain pushing sync block #${block_num} ${block_hash}, head is ${head}", + ("block_num", blk_msg.block.block_num()) + ("block_hash", blk_msg.block_id) + ("head", head_block_num)); + else + fc_ilog(fc::logger::get("sync"), + "chain pushing block #${block_num} ${block_hash}, head is ${head}", + ("block_num", blk_msg.block.block_num()) + ("block_hash", blk_msg.block_id) + ("head", head_block_num)); + + try { + // TODO: in the case where this block is valid but on a fork that's too old for us to switch to, + // you can help the network code out by throwing a block_older_than_undo_history exception. + // when the net code sees that, it will stop trying to push blocks from that chain, but + // leave that peer connected so that they can get sync blocks from us + bool result = chain.accept_block( blk_msg.block, sync_mode, ( block_producer | force_validate ) ? chain::database::skip_nothing : chain::database::skip_transaction_signatures ); + + if( !sync_mode ) + { + fc::microseconds offset = fc::time_point::now() - blk_msg.block.timestamp; + STATSD_TIMER( "p2p", "offset", "block_arrival", offset, 1.0f ) + ilog( "Got ${t} transactions on block ${b} by ${w} -- Block Time Offset: ${l} ms", + ("t", blk_msg.block.transactions.size()) + ("b", blk_msg.block.block_num()) + ("w", blk_msg.block.witness) + ("l", offset.count() / 1000) ); + } + + return result; + } catch ( const chain::unlinkable_block_exception& e ) { + // translate to a graphene::net exception + fc_elog(fc::logger::get("sync"), + "Error when pushing block, current head block is ${head}:\n${e}", + ("e", e.to_detail_string()) + ("head", head_block_num)); + elog("Error when pushing block:\n${e}", ("e", e.to_detail_string())); + FC_THROW_EXCEPTION(graphene::net::unlinkable_block_exception, "Error when pushing block:\n${e}", ("e", e.to_detail_string())); + } catch( const fc::exception& e ) { + fc_elog(fc::logger::get("sync"), + "Error when pushing block, current head block is ${head}:\n${e}", + ("e", e.to_detail_string()) + ("head", head_block_num)); + elog("Error when pushing block:\n${e}", ("e", e.to_detail_string())); + if (e.code() == 4080000) { + elog("Rethrowing as graphene::net exception"); + FC_THROW_EXCEPTION(graphene::net::unlinkable_block_exception, "Error when pushing block:\n${e}", ("e", e.to_detail_string())); + } else { + throw; + } + } + } + else + { + ilog("Block ignored due to start p2p_plugin shutdown"); + FC_THROW("Preventing further processing of ignored block..."); + } + return false; +} FC_CAPTURE_AND_RETHROW( (blk_msg)(sync_mode) ) } + +void p2p_plugin_impl::handle_transaction( const graphene::net::trx_message& trx_msg ) +{ + if( shutdown_helper.get_running().load() ) + { + try + { + action_catcher ac( shutdown_helper.get_running(), shutdown_helper.get_state( HIVE_P2P_TRANSACTION_HANDLER ) ); + + chain.accept_transaction( trx_msg.trx ); + + } FC_CAPTURE_AND_RETHROW( (trx_msg) ) + } + else + { + ilog("Transaction ignored due to start p2p_plugin shutdown"); + FC_THROW("Preventing further processing of ignored transaction..."); + } +} + +void p2p_plugin_impl::handle_message( const graphene::net::message& message_to_process ) +{ + // not a transaction, not a block + FC_THROW( "Invalid Message Type" ); +} + +std::vector< graphene::net::item_hash_t > p2p_plugin_impl::get_block_ids( const std::vector< graphene::net::item_hash_t >& blockchain_synopsis, uint32_t& remaining_item_count, uint32_t limit ) +{ try { + return chain.db().with_read_lock( [&]() + { + vector result; + remaining_item_count = 0; + if( chain.db().head_block_num() == 0 ) + return result; + + result.reserve( limit ); + block_id_type last_known_block_id; + + if( blockchain_synopsis.empty() + || ( blockchain_synopsis.size() == 1 && blockchain_synopsis[0] == block_id_type() ) ) + { + // peer has sent us an empty synopsis meaning they have no blocks. + // A bug in old versions would cause them to send a synopsis containing block 000000000 + // when they had an empty blockchain, so pretend they sent the right thing here. + // do nothing, leave last_known_block_id set to zero + } + else + { + bool found_a_block_in_synopsis = false; + + for( const item_hash_t& block_id_in_synopsis : boost::adaptors::reverse(blockchain_synopsis) ) + { + if (block_id_in_synopsis == block_id_type() || + (chain.db().is_known_block(block_id_in_synopsis) && is_included_block(block_id_in_synopsis))) + { + last_known_block_id = block_id_in_synopsis; + found_a_block_in_synopsis = true; + break; + } + } + + if (!found_a_block_in_synopsis) + FC_THROW_EXCEPTION(graphene::net::peer_is_on_an_unreachable_fork, "Unable to provide a list of blocks starting at any of the blocks in peer's synopsis"); + } + + for( uint32_t num = block_header::num_from_id(last_known_block_id); + num <= chain.db().head_block_num() && result.size() < limit; + ++num ) + { + if( num > 0 ) + result.push_back(chain.db().get_block_id_for_num(num)); + } + + if( !result.empty() && block_header::num_from_id(result.back()) < chain.db().head_block_num() ) + remaining_item_count = chain.db().head_block_num() - block_header::num_from_id(result.back()); + + return result; + }); +} FC_CAPTURE_AND_RETHROW( (blockchain_synopsis)(remaining_item_count)(limit) ) } + +graphene::net::message p2p_plugin_impl::get_item( const graphene::net::item_id& id ) +{ try { + if( id.item_type == graphene::net::block_message_type ) + { + return chain.db().with_read_lock( [&]() + { + auto opt_block = chain.db().fetch_block_by_id(id.item_hash); + if( !opt_block ) + elog("Couldn't find block ${id} -- corresponding ID in our chain is ${id2}", + ("id", id.item_hash)("id2", chain.db().get_block_id_for_num(block_header::num_from_id(id.item_hash)))); + FC_ASSERT( opt_block.valid() ); + // ilog("Serving up block #${num}", ("num", opt_block->block_num())); + return block_message(*opt_block); + }); + } + return chain.db().with_read_lock( [&]() + { + return trx_message( chain.db().get_recent_transaction( id.item_hash ) ); + }); +} FC_CAPTURE_AND_RETHROW( (id) ) } + +hive::protocol::chain_id_type p2p_plugin_impl::get_old_chain_id() const +{ + return chain.db().get_old_chain_id(); +} + +hive::protocol::chain_id_type p2p_plugin_impl::get_new_chain_id() const +{ + return chain.db().get_new_chain_id(); +} + +hive::protocol::chain_id_type p2p_plugin_impl::get_chain_id() const +{ + return chain.db().get_chain_id(); +} + +std::vector< graphene::net::item_hash_t > p2p_plugin_impl::get_blockchain_synopsis( const graphene::net::item_hash_t& reference_point, uint32_t number_of_blocks_after_reference_point ) +{ + try { + std::vector synopsis; + chain.db().with_read_lock( [&]() + { + synopsis.reserve(30); + uint32_t high_block_num; + uint32_t non_fork_high_block_num; + uint32_t low_block_num = chain.db().get_last_irreversible_block_num(); + std::vector fork_history; + + if (reference_point != item_hash_t()) + { + // the node is asking for a summary of the block chain up to a specified + // block, which may or may not be on a fork + // for now, assume it's not on a fork + if (is_included_block(reference_point)) + { + // reference_point is a block we know about and is on the main chain + uint32_t reference_point_block_num = block_header::num_from_id(reference_point); + assert(reference_point_block_num > 0); + high_block_num = reference_point_block_num; + non_fork_high_block_num = high_block_num; + + if (reference_point_block_num < low_block_num) + { + // we're on the same fork (at least as far as reference_point) but we've passed + // reference point and could no longer undo that far if we diverged after that + // block. This should probably only happen due to a race condition where + // the network thread calls this function, and then immediately pushes a bunch of blocks, + // then the main thread finally processes this function. + // with the current framework, there's not much we can do to tell the network + // thread what our current head block is, so we'll just pretend that + // our head is actually the reference point. + // this *may* enable us to fetch blocks that we're unable to push, but that should + // be a rare case (and correctly handled) + low_block_num = reference_point_block_num; + } + } + else + { + // block is a block we know about, but it is on a fork + try + { + fork_history = chain.db().get_block_ids_on_fork(reference_point); + // returns a vector where the last element is the common ancestor with the preferred chain, + // and the first element is the reference point you passed in + assert(fork_history.size() >= 2); + + if( fork_history.front() != reference_point ) + { + edump( (fork_history)(reference_point) ); + assert(fork_history.front() == reference_point); + } + block_id_type last_non_fork_block = fork_history.back(); + fork_history.pop_back(); // remove the common ancestor + boost::reverse(fork_history); + + if (last_non_fork_block == block_id_type()) // if the fork goes all the way back to genesis (does graphene's fork db allow this?) + non_fork_high_block_num = 0; + else + non_fork_high_block_num = block_header::num_from_id(last_non_fork_block); + + high_block_num = non_fork_high_block_num + fork_history.size(); + assert(high_block_num == block_header::num_from_id(fork_history.back())); + } + catch (const fc::exception& e) + { + // unable to get fork history for some reason. maybe not linked? + // we can't return a synopsis of its chain + elog("Unable to construct a blockchain synopsis for reference hash ${hash}: ${exception}", ("hash", reference_point)("exception", e)); + throw; + } + if (non_fork_high_block_num < low_block_num) + { + wlog("Unable to generate a usable synopsis because the peer we're generating it for forked too long ago " + "(our chains diverge after block #${non_fork_high_block_num} but only undoable to block #${low_block_num})", + ("low_block_num", low_block_num) + ("non_fork_high_block_num", non_fork_high_block_num)); + FC_THROW_EXCEPTION(graphene::net::block_older_than_undo_history, "Peer is are on a fork I'm unable to switch to"); + } + } + } + else + { + // no reference point specified, summarize the whole block chain + high_block_num = chain.db().head_block_num(); + non_fork_high_block_num = high_block_num; + if (high_block_num == 0) + return; // we have no blocks + } + + if( low_block_num == 0) + low_block_num = 1; + + // at this point: + // low_block_num is the block before the first block we can undo, + // non_fork_high_block_num is the block before the fork (if the peer is on a fork, or otherwise it is the same as high_block_num) + // high_block_num is the block number of the reference block, or the end of the chain if no reference provided + + // true_high_block_num is the ending block number after the network code appends any item ids it + // knows about that we don't + uint32_t true_high_block_num = high_block_num + number_of_blocks_after_reference_point; + do + { + // for each block in the synopsis, figure out where to pull the block id from. + // if it's <= non_fork_high_block_num, we grab it from the main blockchain; + // if it's not, we pull it from the fork history + if( low_block_num <= non_fork_high_block_num ) + synopsis.push_back(chain.db().get_block_id_for_num(low_block_num)); + else + synopsis.push_back(fork_history[low_block_num - non_fork_high_block_num - 1]); + low_block_num += (true_high_block_num - low_block_num + 2) / 2; + } + while( low_block_num <= high_block_num ); + + //idump((synopsis)); + return; + }); + + return synopsis; +} FC_LOG_AND_RETHROW() } + +void p2p_plugin_impl::sync_status( uint32_t item_type, uint32_t item_count ) +{ + // any status reports to GUI go here +} + +void p2p_plugin_impl::connection_count_changed( uint32_t peer_count ) +{ + // any status reports to GUI go here + chain.connection_count_changed(peer_count); +} + +uint32_t p2p_plugin_impl::get_block_number( const graphene::net::item_hash_t& block_id ) +{ + try { + return block_header::num_from_id(block_id); +} FC_CAPTURE_AND_RETHROW( (block_id) ) } + +fc::time_point_sec p2p_plugin_impl::get_block_time( const graphene::net::item_hash_t& block_id ) +{ + try + { + return chain.db().with_read_lock( [&]() + { + auto opt_block = chain.db().fetch_block_by_id( block_id ); + if( opt_block.valid() ) return opt_block->timestamp; + return fc::time_point_sec::min(); + }); + } FC_CAPTURE_AND_RETHROW( (block_id) ) +} + +graphene::net::item_hash_t p2p_plugin_impl::get_head_block_id() const +{ try { + return chain.db().with_read_lock( [&]() + { + return chain.db().head_block_id(); + }); +} FC_CAPTURE_AND_RETHROW() } + +uint32_t p2p_plugin_impl::estimate_last_known_fork_from_git_revision_timestamp(uint32_t) const +{ + return 0; // there are no forks in graphene +} + +void p2p_plugin_impl::error_encountered( const string& message, const fc::oexception& error ) +{ + // notify GUI or something cool +} + +fc::time_point_sec p2p_plugin_impl::get_blockchain_now() +{ try { + return fc::time_point::now(); +} FC_CAPTURE_AND_RETHROW() } + +bool p2p_plugin_impl::is_included_block(const block_id_type& block_id) +{ try { + return chain.db().with_read_lock( [&]() + { + uint32_t block_num = block_header::num_from_id(block_id); + block_id_type block_id_in_preferred_chain = chain.db().get_block_id_for_num(block_num); + return block_id == block_id_in_preferred_chain; + }); +} FC_CAPTURE_AND_RETHROW() } + +////////////////////////////// End node_delegate Implementation ////////////////////////////// + +} // detail + +p2p_plugin::p2p_plugin() +{ + set_pre_shutdown_order( p2p_order ); +} + +p2p_plugin::~p2p_plugin() {} + +void p2p_plugin::set_program_options( bpo::options_description& cli, bpo::options_description& cfg) +{ + std::stringstream seed_ss; + for( auto& s : default_seeds ) + { + seed_ss << s << ' '; + } + + cfg.add_options() + ("p2p-endpoint", bpo::value()->implicit_value("127.0.0.1:9876"), "The local IP address and port to listen for incoming connections.") + ("p2p-max-connections", bpo::value(), "Maxmimum number of incoming connections on P2P endpoint.") + ("seed-node", bpo::value>()->composing(), "The IP address and port of a remote peer to sync with. Deprecated in favor of p2p-seed-node.") + ("p2p-seed-node", bpo::value>()->composing()->default_value( default_seeds, seed_ss.str() ), "The IP address and port of a remote peer to sync with.") + ("p2p-parameters", bpo::value(), ("P2P network parameters. (Default: " + fc::json::to_string(graphene::net::node_configuration()) + " )").c_str() ) + ; + cli.add_options() + ("force-validate", bpo::bool_switch()->default_value(false), "Force validation of all transactions. Deprecated in favor of p2p-force-validate" ) + ("p2p-force-validate", bpo::bool_switch()->default_value(false), "Force validation of all transactions." ) + ; +} + +void p2p_plugin::plugin_initialize(const boost::program_options::variables_map& options) +{ + my = std::make_unique< detail::p2p_plugin_impl >( appbase::app().get_plugin< plugins::chain::chain_plugin >() ); + + if( options.count( "p2p-endpoint" ) ) + my->endpoint = fc::ip::endpoint::from_string( options.at( "p2p-endpoint" ).as< string >() ); + + my->user_agent = "Hive Reference Implementation"; + + if( options.count( "p2p-max-connections" ) ) + my->max_connections = options.at( "p2p-max-connections" ).as< uint32_t >(); + + if( options.count( "seed-node" ) || options.count( "p2p-seed-node" ) ) + { + vector< string > seeds; + if( options.count( "seed-node" ) ) + { + wlog( "Option seed-node is deprecated in favor of p2p-seed-node" ); + auto s = options.at("seed-node").as>(); + for( auto& arg : s ) + { + vector< string > addresses; + boost::split( addresses, arg, boost::is_any_of( " \t" ) ); + seeds.insert( seeds.end(), addresses.begin(), addresses.end() ); + } + } + + if( options.count( "p2p-seed-node" ) ) + { + auto s = options.at("p2p-seed-node").as>(); + for( auto& arg : s ) + { + vector< string > addresses; + boost::split( addresses, arg, boost::is_any_of( " \t" ) ); + seeds.insert( seeds.end(), addresses.begin(), addresses.end() ); + } + } + + for( const string& endpoint_string : seeds ) + { + try + { + std::vector endpoints = detail::resolve_string_to_ip_endpoints(endpoint_string); + my->seeds.insert( my->seeds.end(), endpoints.begin(), endpoints.end() ); + } + catch( const fc::exception& e ) + { + wlog( "caught exception ${e} while adding seed node ${endpoint}", + ("e", e.to_detail_string())("endpoint", endpoint_string) ); + } + } + } + + my->force_validate = options.at( "p2p-force-validate" ).as< bool >(); + + if( !my->force_validate && options.at( "force-validate" ).as< bool >() ) + { + wlog( "Option force-validate is deprecated in favor of p2p-force-validate" ); + my->force_validate = true; + } + + if( options.count("p2p-parameters") ) + { + fc::variant var = fc::json::from_string( options.at("p2p-parameters").as(), fc::json::strict_parser ); + my->config = var.get_object(); + } +} + +void p2p_plugin::plugin_startup() +{ + if( !my->chain.is_p2p_enabled() ) + return; + + my->p2p_thread.async( [this] + { + my->node.reset(new graphene::net::node(my->user_agent)); + my->node->load_configuration(app().data_dir() / "p2p"); + my->node->set_node_delegate( &(*my) ); + + if( my->endpoint ) + { + ilog("Configuring P2P to listen at ${ep}", ("ep", my->endpoint)); + my->node->listen_on_endpoint(*my->endpoint, true); + } + + for( const auto& seed : my->seeds ) + { + try + { + ilog("P2P adding seed node ${s}", ("s", seed)); + my->node->add_node(seed); + //don't connect to seed nodes until we've started p2p network + //my->node->connect_to_endpoint(seed); + } + catch( graphene::net::already_connected_to_requested_peer& ) + { + wlog( "Already connected to seed node ${s}. Is it specified twice in config?", ("s", seed) ); + } + } + + if( my->max_connections ) + { + if( my->config.find( "maximum_number_of_connections" ) != my->config.end() ) + ilog( "Overriding advanded_node_parameters[ \"maximum_number_of_connections\" ] with ${cons}", ("cons", my->max_connections) ); + + my->config.set( "maximum_number_of_connections", fc::variant( my->max_connections ) ); + } + + my->node->set_advanced_node_parameters( my->config ); + my->node->listen_to_p2p_network(); + my->node->connect_to_p2p_network(); + block_id_type block_id; + my->chain.db().with_read_lock( [&]() + { + block_id = my->chain.db().head_block_id(); + }); + my->node->sync_from(graphene::net::item_id(graphene::net::block_message_type, block_id), std::vector()); + ilog("P2P node listening at ${ep}", ("ep", my->node->get_actual_listening_endpoint())); + }).wait(); + ilog( "P2P Plugin started" ); +} + +void p2p_plugin::plugin_pre_shutdown() { + + if( !my->chain.is_p2p_enabled() ) + return; + + ilog("Shutting down P2P Plugin"); + + my->shutdown_helper.prepare_shutdown(); + my->shutdown_helper.wait(); + + ilog("P2P Plugin: checking handle_block and handle_transaction activity"); + my->node->close(); + fc::promise::ptr quitDone(new fc::promise("P2P thread quit")); + my->p2p_thread.quit(quitDone.get()); + ilog("Waiting for p2p_thread quit"); + quitDone->wait(); + ilog("p2p_thread quit done"); + my->node.reset(); +} + +void p2p_plugin::plugin_shutdown() +{ + //Nothing to do. Everything is closed/finished during `pre_shutdown` stage, +} + +void p2p_plugin::broadcast_block( const hive::protocol::signed_block& block ) +{ + ulog("Broadcasting block #${n}", ("n", block.block_num())); + my->node->broadcast( graphene::net::block_message( block ) ); +} + +void p2p_plugin::broadcast_transaction( const hive::protocol::signed_transaction& tx ) +{ + ulog("Broadcasting tx #${n}", ("id", tx.id())); + my->node->broadcast( graphene::net::trx_message( tx ) ); +} + +void p2p_plugin::set_block_production( bool producing_blocks ) +{ + my->block_producer = producing_blocks; +} + +fc::variant_object p2p_plugin::get_info() +{ + fc::mutable_variant_object result = my->node->network_get_info(); + result["connection_count"] = my->node->get_connection_count(); + return result; +} + +void p2p_plugin::add_node(const fc::ip::endpoint& endpoint) +{ + my->node->add_node(endpoint); +} + +void p2p_plugin::set_allowed_peers(const std::vector& allowed_peers) +{ + my->node->set_allowed_peers(allowed_peers); +} + +std::vector< api_peer_status > p2p_plugin::get_connected_peers() +{ + std::vector connected_peers = my->node->get_connected_peers(); + std::vector api_connected_peers; + api_connected_peers.reserve( connected_peers.size() ); + for (const graphene::net::peer_status& peer : connected_peers) + api_connected_peers.emplace_back(peer.version, peer.host, peer.info); + return api_connected_peers; +} + +} } } // namespace hive::plugins::p2p diff --git a/libraries/plugins/rc/include/hive/plugins/rc/resource_count.hpp b/libraries/plugins/rc/include/hive/plugins/rc/resource_count.hpp index 96e16478c5..a4e216a387 100644 --- a/libraries/plugins/rc/include/hive/plugins/rc/resource_count.hpp +++ b/libraries/plugins/rc/include/hive/plugins/rc/resource_count.hpp @@ -29,11 +29,13 @@ struct count_resources_result void count_resources( const hive::protocol::signed_transaction& tx, - count_resources_result& result ); + count_resources_result& result + ); void count_resources( const hive::protocol::optional_automated_action&, - count_resources_result& result ); + count_resources_result& result + ); } } } // hive::plugins::rc diff --git a/libraries/plugins/rc/include/hive/plugins/rc/resource_sizes.hpp b/libraries/plugins/rc/include/hive/plugins/rc/resource_sizes.hpp index 6eb08fca45..b8c85c7a39 100644 --- a/libraries/plugins/rc/include/hive/plugins/rc/resource_sizes.hpp +++ b/libraries/plugins/rc/include/hive/plugins/rc/resource_sizes.hpp @@ -55,6 +55,8 @@ struct state_object_size_info // convert_request_object int64_t convert_request_object_base_size = 48 *STATE_BYTES_SCALE; + // collateralized_convert_request_object + int64_t collateralized_convert_request_object_base_size = 48 * STATE_BYTES_SCALE; // decline_voting_rights_request_object int64_t decline_voting_rights_request_object_base_size = 28*STATE_BYTES_SCALE; @@ -93,6 +95,10 @@ struct state_object_size_info // proposal_vote_object int64_t proposal_vote_object_base_size = 24 *STATE_BYTES_SCALE; int64_t proposal_vote_object_member_size = 8 *STATE_BYTES_SCALE; + + // recurrent_transfer_operation + int64_t recurrent_transfer_object_base_size = 80 * STATE_BYTES_SCALE; + }; struct operation_exec_info @@ -110,6 +116,7 @@ struct operation_exec_info int64_t comment_operation_exec_time = 114100; int64_t comment_options_operation_exec_time = 13200; int64_t convert_operation_exec_time = 15700; + int64_t collateralized_convert_operation_exec_time = 15700; int64_t create_claimed_account_operation_exec_time = 57700; int64_t custom_operation_exec_time = 11400; int64_t custom_json_operation_exec_time = 11400; @@ -135,6 +142,7 @@ struct operation_exec_info int64_t withdraw_vesting_operation_exec_time = 10400; int64_t witness_set_properties_operation_exec_time = 9500; int64_t witness_update_operation_exec_time = 9500; + int64_t recurrent_transfer_operation_exec_time = 17200; #ifdef HIVE_ENABLE_SMT int64_t claim_reward_balance2_operation_exec_time = 0; @@ -147,7 +155,7 @@ struct operation_exec_info #endif int64_t create_proposal_operation_exec_time = 31700; - int64_t update_proposal_operation_exec_time = 9600; + int64_t update_proposal_operation_exec_time = 9600; int64_t update_proposal_votes_operation_exec_time = 12000; int64_t remove_proposal_operation_exec_time = 12000; }; @@ -167,6 +175,7 @@ FC_REFLECT( hive::plugins::rc::state_object_size_info, ( comment_object_beneficiaries_member_size ) ( comment_vote_object_base_size ) ( convert_request_object_base_size ) + ( collateralized_convert_request_object_base_size ) ( decline_voting_rights_request_object_base_size ) ( escrow_object_base_size ) ( limit_order_object_base_size ) @@ -198,6 +207,7 @@ FC_REFLECT( hive::plugins::rc::operation_exec_info, ( comment_operation_exec_time ) ( comment_options_operation_exec_time ) ( convert_operation_exec_time ) + ( collateralized_convert_operation_exec_time ) ( create_claimed_account_operation_exec_time ) ( custom_operation_exec_time ) ( custom_json_operation_exec_time ) @@ -238,5 +248,6 @@ FC_REFLECT( hive::plugins::rc::operation_exec_info, (update_proposal_operation_exec_time) (update_proposal_votes_operation_exec_time) (remove_proposal_operation_exec_time) + (recurrent_transfer_operation_exec_time) ) diff --git a/libraries/plugins/rc/rc_plugin.cpp b/libraries/plugins/rc/rc_plugin.cpp index d649627dee..e2bb164767 100644 --- a/libraries/plugins/rc/rc_plugin.cpp +++ b/libraries/plugins/rc/rc_plugin.cpp @@ -235,7 +235,7 @@ void use_account_rcs( rc_plugin_skip_flags skip #ifdef IS_TEST_NET , - set< account_name_type > whitelist + const set< account_name_type >& whitelist #endif ) { @@ -368,7 +368,6 @@ struct block_extensions_count_resources_visitor typedef void result_type; count_resources_result& _r; - block_extensions_count_resources_visitor( count_resources_result& r ) : _r( r ) {} // Only optional actions need to be counted. We decided in design that @@ -378,7 +377,7 @@ struct block_extensions_count_resources_visitor { for( const auto& a : opt_actions ) { - count_resources( a, _r ); + count_resources( a, _r); } } @@ -829,7 +828,7 @@ struct post_apply_operation_visitor database& db, uint32_t t, uint32_t b, - account_name_type w + const account_name_type& w ) : _mod_accounts(ma), _db(db), _current_time(t), _current_block_number(b), _current_witness(w) {} diff --git a/libraries/plugins/rc/resource_count.cpp b/libraries/plugins/rc/resource_count.cpp index cd264af410..cccd39734f 100644 --- a/libraries/plugins/rc/resource_count.cpp +++ b/libraries/plugins/rc/resource_count.cpp @@ -87,6 +87,12 @@ struct count_operation_visitor execution_time_count += _e.convert_operation_exec_time; } + void operator()( const collateralized_convert_operation& op ) const + { + state_bytes_count += _w.collateralized_convert_request_object_base_size; + execution_time_count += _e.collateralized_convert_operation_exec_time; + } + void operator()( const create_claimed_account_operation& op )const { state_bytes_count += @@ -351,16 +357,16 @@ struct count_operation_visitor void operator()( const create_proposal_operation& op ) const { state_bytes_count += _w.proposal_object_base_size; - state_bytes_count += sizeof( op.subject ); - state_bytes_count += sizeof( op.permlink ); + state_bytes_count += sizeof( op.subject ) + op.subject.size();// NOLINT(misc-sizeof-container) + state_bytes_count += sizeof( op.permlink ) + op.permlink.size();// NOLINT(misc-sizeof-container) execution_time_count += _e.create_proposal_operation_exec_time; } void operator()( const update_proposal_operation& op ) const { state_bytes_count += _w.proposal_object_base_size; - state_bytes_count += sizeof( op.subject ); - state_bytes_count += sizeof( op.permlink ); + state_bytes_count += sizeof( op.subject ) + op.subject.size();// NOLINT(misc-sizeof-container) + state_bytes_count += sizeof( op.permlink ) + op.permlink.size();// NOLINT(misc-sizeof-container) execution_time_count += _e.update_proposal_operation_exec_time; } @@ -376,6 +382,13 @@ struct count_operation_visitor execution_time_count += _e.remove_proposal_operation_exec_time; } + void operator()( const recurrent_transfer_operation& op )const + { + state_bytes_count += _w.recurrent_transfer_object_base_size; + execution_time_count += _e.recurrent_transfer_operation_exec_time * op.executions; + market_op_count++; + } + void operator()( const recover_account_operation& ) const {} void operator()( const pow_operation& ) const {} void operator()( const pow2_operation& ) const {} @@ -385,6 +398,7 @@ struct count_operation_visitor // Virtual Ops void operator()( const fill_convert_request_operation& ) const {} + void operator()( const fill_collateralized_convert_request_operation& ) const {} void operator()( const author_reward_operation& ) const {} void operator()( const curation_reward_operation& ) const {} void operator()( const comment_reward_operation& ) const {} @@ -396,19 +410,28 @@ struct count_operation_visitor void operator()( const fill_transfer_from_savings_operation& ) const {} void operator()( const hardfork_operation& ) const {} void operator()( const comment_payout_update_operation& ) const {} - void operator()(const effective_comment_vote_operation&) const {} - void operator()(const ineffective_delete_comment_operation&) const {} + void operator()( const effective_comment_vote_operation& ) const {} + void operator()( const ineffective_delete_comment_operation& ) const {} void operator()( const return_vesting_delegation_operation& ) const {} void operator()( const comment_benefactor_reward_operation& ) const {} void operator()( const producer_reward_operation& ) const {} void operator()( const clear_null_account_balance_operation& ) const {} void operator()( const consolidate_treasury_balance_operation& ) const {} void operator()( const delayed_voting_operation& ) const {} + void operator()( const transfer_to_vesting_completed_operation& ) const {} + void operator()( const pow_reward_operation& ) const {} + void operator()( const vesting_shares_split_operation& ) const {} + void operator()( const account_created_operation& ) const {} void operator()( const proposal_pay_operation& ) const {} void operator()( const sps_fund_operation& ) const {} void operator()( const sps_convert_operation& ) const {} void operator()( const hardfork_hive_operation& ) const {} void operator()( const hardfork_hive_restore_operation& ) const {} + void operator()( const expired_account_notification_operation& ) const {} + void operator()( const changed_recovery_account_operation& ) const {} + void operator()( const system_warning_operation& ) const {} + void operator()( const fill_recurrent_transfer_operation& ) const {} + void operator()( const failed_recurrent_transfer_operation& ) const {} // Optional Actions #ifdef IS_TEST_NET @@ -418,7 +441,7 @@ struct count_operation_visitor // TODO: // Should following ops be market ops? - // withdraw_vesting, convert, set_withdraw_vesting_route, limit_order_create2 + // withdraw_vesting, convert, collateralized_convert, set_withdraw_vesting_route, limit_order_create2 // escrow_transfer, escrow_dispute, escrow_release, escrow_approve, // transfer_to_savings, transfer_from_savings, cancel_transfer_from_savings, // claim_reward_balance, delegate_vesting_shares, any SMT operations @@ -428,7 +451,8 @@ typedef count_operation_visitor count_optional_action_visitor; void count_resources( const signed_transaction& tx, - count_resources_result& result ) + count_resources_result& result + ) { static const state_object_size_info size_info; static const operation_exec_info exec_info; @@ -457,7 +481,7 @@ void count_resources( void count_resources( const optional_automated_action& action, - count_resources_result& result ) + count_resources_result& result) { static const state_object_size_info size_info; static const operation_exec_info exec_info; diff --git a/libraries/plugins/state_snapshot/state_snapshot_plugin.cpp b/libraries/plugins/state_snapshot/state_snapshot_plugin.cpp index 2cbde7e047..012231136d 100644 --- a/libraries/plugins/state_snapshot/state_snapshot_plugin.cpp +++ b/libraries/plugins/state_snapshot/state_snapshot_plugin.cpp @@ -147,7 +147,7 @@ rocksdb_cleanup_helper rocksdb_cleanup_helper::open(const ::rocksdb::Options& op throw std::exception(); } - return std::move(retVal); + return retVal; } void rocksdb_cleanup_helper::close() @@ -650,7 +650,7 @@ index_dump_writer::prepare(const std::string& indexDescription, size_t firstId, right += ITEMS_PER_WORKER; } - return std::move(retVal); + return retVal; } void index_dump_writer::start(const workers& workers) @@ -855,7 +855,7 @@ index_dump_reader::prepare(const std::string& indexDescription, snapshot_convert workers retVal; retVal.emplace_back(_builtWorkers.front().get()); - return std::move(retVal); + return retVal; } void index_dump_reader::start(const workers& workers) @@ -915,7 +915,7 @@ class state_snapshot_plugin::impl final : protected chain::state_snapshot_provid void store_snapshot_manifest(const bfs::path& actualStoragePath, const std::vector>& builtWriters, const snapshot_dump_supplement_helper& dumpHelper) const; - std::pair load_snapshot_manifest(const bfs::path& actualStoragePath); + std::tuple load_snapshot_manifest(const bfs::path& actualStoragePath); void load_snapshot_external_data(const plugin_external_data_index& idx); private: @@ -991,6 +991,7 @@ void state_snapshot_plugin::impl::store_snapshot_manifest(const bfs::path& actua rocksdb_cleanup_helper db = rocksdb_cleanup_helper::open(dbOptions, manifestDbPath); ::rocksdb::ColumnFamilyHandle* manifestCF = db.create_column_family("INDEX_MANIFEST"); ::rocksdb::ColumnFamilyHandle* externalDataCF = db.create_column_family("EXTERNAL_DATA"); + ::rocksdb::ColumnFamilyHandle* snapshotManifestCF = db.create_column_family("IRREVERSIBLE_STATE"); ::rocksdb::WriteOptions writeOptions; @@ -1039,14 +1040,28 @@ void state_snapshot_plugin::impl::store_snapshot_manifest(const bfs::path& actua throw std::exception(); } + } + + { + Slice key("LAST_IRREVERSIBLE_BLOCK"); + uint32_t lib = _mainDb.get_last_irreversible_block_num(); + Slice value(reinterpret_cast(&lib), sizeof(uint32_t)); + auto status = db->Put(writeOptions, snapshotManifestCF, key, value); + + if(status.ok() == false) + { + elog("Cannot write an index manifest entry to output file: `${p}'. Error details: `${e}'.", ("p", manifestDbPath.string())("e", status.ToString())); + ilog("Failing key value: \"LAST_IRREVERSIBLE_BLOCK\""); + throw std::exception(); + } } db.close(); } -std::pair state_snapshot_plugin::impl::load_snapshot_manifest(const bfs::path& actualStoragePath) - { +std::tuple state_snapshot_plugin::impl::load_snapshot_manifest(const bfs::path& actualStoragePath) +{ bfs::path manifestDbPath(actualStoragePath); manifestDbPath /= "snapshot-manifest"; @@ -1065,6 +1080,10 @@ std::pair state_snapshot_plugin:: cfDescriptor.name = "EXTERNAL_DATA"; cfDescriptors.push_back(cfDescriptor); + cfDescriptor = ::rocksdb::ColumnFamilyDescriptor(); + cfDescriptor.name = "IRREVERSIBLE_STATE"; + cfDescriptors.push_back(cfDescriptor); + std::vector<::rocksdb::ColumnFamilyHandle*> cfHandles; std::unique_ptr<::rocksdb::DB> manifestDbPtr; ::rocksdb::DB* manifestDb = nullptr; @@ -1085,29 +1104,29 @@ std::pair state_snapshot_plugin:: snapshot_manifest retVal; { - ::rocksdb::ReadOptions rOptions; + ::rocksdb::ReadOptions rOptions; - std::unique_ptr<::rocksdb::Iterator> indexIterator(manifestDb->NewIterator(rOptions, cfHandles[1])); + std::unique_ptr<::rocksdb::Iterator> indexIterator(manifestDb->NewIterator(rOptions, cfHandles[1])); - std::vector buffer; - for(indexIterator->SeekToFirst(); indexIterator->Valid(); indexIterator->Next()) + std::vector buffer; + for(indexIterator->SeekToFirst(); indexIterator->Valid(); indexIterator->Next()) { - auto keySlice = indexIterator->key(); - auto valueSlice = indexIterator->value(); + auto keySlice = indexIterator->key(); + auto valueSlice = indexIterator->value(); - buffer.insert(buffer.end(), valueSlice.data(), valueSlice.data() + valueSlice.size()); + buffer.insert(buffer.end(), valueSlice.data(), valueSlice.data() + valueSlice.size()); - index_manifest_info info; + index_manifest_info info; - chainbase::serialization::unpack_from_buffer(info, buffer); + chainbase::serialization::unpack_from_buffer(info, buffer); - FC_ASSERT(keySlice.ToString() == info.name); + FC_ASSERT(keySlice.ToString() == info.name); - ilog("Loaded manifest info for index ${i} having storage files: ${sf}", ("i", info.name)("sf", info.storage_files)); + ilog("Loaded manifest info for index ${i} having storage files: ${sf}", ("i", info.name)("sf", info.storage_files)); - retVal.emplace(std::move(info)); + retVal.emplace(std::move(info)); - buffer.clear(); + buffer.clear(); } } @@ -1149,20 +1168,41 @@ std::pair state_snapshot_plugin:: } } + uint32_t lib = 0; + + { + ::rocksdb::ReadOptions rOptions; + + std::unique_ptr<::rocksdb::Iterator> irreversibleStateIterator(manifestDb->NewIterator(rOptions, cfHandles[3])); + irreversibleStateIterator->SeekToFirst(); + FC_ASSERT(irreversibleStateIterator->Valid(), "No entry for IRREVERSIBLE_STATE. Probably used old snapshot format (must be regenerated)."); + + std::vector buffer; + auto valueSlice = irreversibleStateIterator->value(); + + buffer.insert(buffer.end(), valueSlice.data(), valueSlice.data() + valueSlice.size()); + chainbase::serialization::unpack_from_buffer(lib, buffer); + buffer.clear(); + //ilog("lib: ${s}", ("s", lib)); + + irreversibleStateIterator->Next(); + FC_ASSERT(irreversibleStateIterator->Valid() == false, "Multiple entries specifying irreversible block ?"); + } + for(auto* cfh : cfHandles) - { + { status = manifestDb->DestroyColumnFamilyHandle(cfh); if(status.ok() == false) - { + { elog("Cannot destroy column family handle...'. Error details: `${e}'.", ("e", status.ToString())); - } } + } manifestDb->Close(); manifestDbPtr.release(); - return std::make_pair(retVal, extDataIdx); - } + return std::make_tuple(retVal, extDataIdx, lib); +} void state_snapshot_plugin::impl::load_snapshot_external_data(const plugin_external_data_index& idx) { @@ -1200,11 +1240,7 @@ void state_snapshot_plugin::impl::prepare_snapshot(const std::string& snapshotNa bfs::create_directories(actualStoragePath); else { - if( !bfs::is_empty(actualStoragePath) ) - { - wlog("Directory ${p} is not empty. Creating snapshot rejected.", ("p", actualStoragePath.string())); - return; - } + FC_ASSERT(bfs::is_empty(actualStoragePath), "Directory ${p} is not empty. Creating snapshot rejected.", ("p", actualStoragePath.string())); } @@ -1305,7 +1341,7 @@ void state_snapshot_plugin::impl::load_snapshot(const std::string& snapshotName, for(chainbase::abstract_index* idx : indices) { - builtReaders.emplace_back(std::make_unique(snapshotManifest.first, actualStoragePath)); + builtReaders.emplace_back(std::make_unique(std::get<0>(snapshotManifest), actualStoragePath)); index_dump_reader* reader = builtReaders.back().get(); if(_allow_concurrency) @@ -1321,18 +1357,23 @@ void state_snapshot_plugin::impl::load_snapshot(const std::string& snapshotName, threadpool.join_all(); - if(snapshotManifest.second.empty()) + plugin_external_data_index& extDataIdx = std::get<1>(snapshotManifest); + if(extDataIdx.empty()) { ilog("Skipping external data load due to lack of data saved to the snapshot"); } else { - load_snapshot_external_data(snapshotManifest.second); + load_snapshot_external_data(extDataIdx); } + auto last_irr_block = std::get<2>(snapshotManifest); + // set irreversible block number after database::resetState + _mainDb.set_last_irreversible_block_num(last_irr_block); + auto blockNo = _mainDb.head_block_num(); - ilog("Setting chainbase revision to ${b} block...", ("b", blockNo)); + ilog("Setting chainbase revision to ${b} block... Loaded irreversible block is: ${lib}.", ("b", blockNo)("lib", last_irr_block)); _mainDb.set_revision(blockNo); const auto& measure = dumper.measure(blockNo, [](benchmark_dumper::index_memory_details_cntr_t&, bool) {}); diff --git a/libraries/plugins/statsd/statsd_plugin.cpp b/libraries/plugins/statsd/statsd_plugin.cpp index 909a138544..38e566d7b7 100644 --- a/libraries/plugins/statsd/statsd_plugin.cpp +++ b/libraries/plugins/statsd/statsd_plugin.cpp @@ -38,7 +38,7 @@ namespace detail bool is_accessible() const; bool filter_by_namespace( const std::string& ns, const std::string& stat ) const; - void execute_operation( const std::string& ns, const std::string& stat, std::function< void() > op ) const; + void execute_operation( const std::string& ns, const std::string& stat, const std::function< void() >& op ) const; void increment( const std::string& ns, const std::string& stat, const std::string& key, const float frequency ) const noexcept; void decrement( const std::string& ns, const std::string& stat, const std::string& key, const float frequency ) const noexcept; @@ -132,7 +132,7 @@ namespace detail return _blacklist != found; } - void statsd_plugin_impl::execute_operation( const std::string& ns, const std::string& stat, std::function< void() > op ) const + void statsd_plugin_impl::execute_operation( const std::string& ns, const std::string& stat, const std::function< void() >& op ) const { if( !is_accessible() ) return; if( !filter_by_namespace( ns, stat ) ) return; diff --git a/libraries/plugins/webserver/include/hive/plugins/webserver/local_endpoint.hpp b/libraries/plugins/webserver/include/hive/plugins/webserver/local_endpoint.hpp index 32bc6c454e..234de26e91 100644 --- a/libraries/plugins/webserver/include/hive/plugins/webserver/local_endpoint.hpp +++ b/libraries/plugins/webserver/include/hive/plugins/webserver/local_endpoint.hpp @@ -83,8 +83,7 @@ class local_connection : public lib::enable_shared_from_this { return socket::make_error_code(socket::error::invalid_state); } - m_socket = lib::make_shared( - lib::ref(*service)); + m_socket.reset(new lib::asio::local::stream_protocol::socket(*service)); m_state = READY; @@ -268,8 +267,7 @@ class local_endpoint : public config::socket_type { m_alog->write(log::alevel::devel,"asio::init_asio"); m_io_service = ptr; - m_acceptor = lib::make_shared( - lib::ref(*m_io_service)); + m_acceptor.reset(new lib::asio::local::stream_protocol::acceptor(*m_io_service)); m_state = READY; ec = lib::error_code(); @@ -515,9 +513,7 @@ class local_endpoint : public config::socket_type { * @since 0.3.0 */ void start_perpetual() { - m_work = lib::make_shared( - lib::ref(*m_io_service) - ); + m_work.reset(new lib::asio::io_service::work(*m_io_service)); } /// Clears the endpoint's perpetual flag, allowing it to exit when empty diff --git a/libraries/plugins/webserver/webserver_plugin.cpp b/libraries/plugins/webserver/webserver_plugin.cpp index 92a4137f32..645fd520bf 100644 --- a/libraries/plugins/webserver/webserver_plugin.cpp +++ b/libraries/plugins/webserver/webserver_plugin.cpp @@ -30,7 +30,6 @@ namespace hive { namespace plugins { namespace webserver { namespace asio = boost::asio; -using std::map; using std::string; using boost::optional; using boost::asio::ip::tcp; @@ -139,7 +138,7 @@ class webserver_plugin_impl void start_webserver(); void stop_webserver(); - void handle_ws_message( websocket_server_type*, connection_hdl, detail::websocket_server_type::message_ptr ); + void handle_ws_message( websocket_server_type*, connection_hdl, const detail::websocket_server_type::message_ptr& ); void handle_http_message( websocket_server_type*, connection_hdl ); void handle_http_request( websocket_local_server_type*, connection_hdl ); @@ -306,9 +305,9 @@ void webserver_plugin_impl::stop_webserver() } } -void webserver_plugin_impl::handle_ws_message( websocket_server_type* server, connection_hdl hdl, detail::websocket_server_type::message_ptr msg ) +void webserver_plugin_impl::handle_ws_message( websocket_server_type* server, connection_hdl hdl, const detail::websocket_server_type::message_ptr& msg ) { - auto con = server->get_con_from_hdl( hdl ); + auto con = server->get_con_from_hdl( std::move( hdl ) ); thread_pool_ios.post( [con, msg, this]() { @@ -346,7 +345,7 @@ void webserver_plugin_impl::handle_ws_message( websocket_server_type* server, co void webserver_plugin_impl::handle_http_message( websocket_server_type* server, connection_hdl hdl ) { - auto con = server->get_con_from_hdl( hdl ); + auto con = server->get_con_from_hdl( std::move( hdl ) ); con->defer_http_response(); thread_pool_ios.post( [con, this]() @@ -391,7 +390,7 @@ void webserver_plugin_impl::handle_http_message( websocket_server_type* server, } void webserver_plugin_impl::handle_http_request(websocket_local_server_type* server, connection_hdl hdl ) { - auto con = server->get_con_from_hdl( hdl ); + auto con = server->get_con_from_hdl( std::move( hdl ) ); con->defer_http_response(); thread_pool_ios.post( [con, this]() diff --git a/libraries/plugins/witness/witness_plugin.cpp b/libraries/plugins/witness/witness_plugin.cpp index 6f6fbd7123..3a6656b3db 100644 --- a/libraries/plugins/witness/witness_plugin.cpp +++ b/libraries/plugins/witness/witness_plugin.cpp @@ -341,7 +341,8 @@ namespace detail { switch(result) { case block_production_condition::produced: - ilog("Generated block #${n} with timestamp ${t} at time ${c}", (capture)); + ilog("Generated block #${n} with timestamp ${t} at time ${c}", ("n", capture["n"])("t", capture["t"])("c", capture["c"])); + break; case block_production_condition::not_synced: // ilog("Not producing block because production is disabled until we receive a recent block (see: --enable-stale-production)"); @@ -353,10 +354,10 @@ namespace detail { // ilog("Not producing block because slot has not yet arrived"); break; case block_production_condition::no_private_key: - ilog("Not producing block because I don't have the private key for ${scheduled_key}", (capture) ); + ilog("Not producing block because I don't have the private key for ${scheduled_key}", ("scheduled_key", capture["scheduled_key"]) ); break; case block_production_condition::low_participation: - elog("Not producing block because node appears to be on a minority fork with only ${pct}% witness participation", (capture) ); + elog("Not producing block because node appears to be on a minority fork with only ${pct}% witness participation", ("pct", capture["pct"]) ); break; case block_production_condition::lag: elog("Not producing block because node didn't wake up within ${t}ms of the slot time.", ("t", BLOCK_PRODUCING_LAG_TIME)); diff --git a/libraries/protocol/CMakeLists.txt b/libraries/protocol/CMakeLists.txt index f8ff7a6859..6be6968f42 100644 --- a/libraries/protocol/CMakeLists.txt +++ b/libraries/protocol/CMakeLists.txt @@ -18,9 +18,19 @@ else( MSVC ) ) endif( MSVC ) +include("${CMAKE_CURRENT_SOURCE_DIR}/get_config.d/generate_get_config.cmake") +generate_get_config( + "${CMAKE_CURRENT_SOURCE_DIR}/include/hive/protocol/config.hpp" # path to config.hpp + "${CMAKE_CURRENT_SOURCE_DIR}/get_config.d/get_config.cpp.in" # path to get_config template file + "${CMAKE_CURRENT_BINARY_DIR}/get_config.cpp" # output path +) + +############# + ## SORT .cpp by most likely to change / break compile add_library( hive_protocol + testnet_blockchain_configuration.cpp smt_operations.cpp hive_operations.cpp sps_operations.cpp @@ -35,7 +45,7 @@ add_library( hive_protocol block.cpp asset.cpp version.cpp - get_config.cpp + ${GET_CONFIG_CPP} ${HEADERS} ${hardfork_hpp_file} diff --git a/libraries/protocol/asset.cpp b/libraries/protocol/asset.cpp index 0833eb7a7e..3de6874aed 100644 --- a/libraries/protocol/asset.cpp +++ b/libraries/protocol/asset.cpp @@ -282,43 +282,67 @@ DEFINE_PRICE_COMPARISON_OPERATOR( <= ) DEFINE_PRICE_COMPARISON_OPERATOR( > ) DEFINE_PRICE_COMPARISON_OPERATOR( >= ) - asset operator * ( const asset& a, const price& b ) - { - if( a.symbol == b.base.symbol ) - { - FC_ASSERT( b.base.amount.value > 0 ); - uint128_t result = (uint128_t(a.amount.value) * b.quote.amount.value)/b.base.amount.value; - FC_ASSERT( result.hi == 0 ); - return asset( result.to_uint64(), b.quote.symbol ); - } - else if( a.symbol == b.quote.symbol ) - { - FC_ASSERT( b.quote.amount.value > 0 ); - uint128_t result = (uint128_t(a.amount.value) * b.base.amount.value)/b.quote.amount.value; - FC_ASSERT( result.hi == 0 ); - return asset( result.to_uint64(), b.base.symbol ); - } - FC_THROW_EXCEPTION( fc::assert_exception, "invalid asset * price", ("asset",a)("price",b) ); - } +asset operator * ( const asset& a, const price& b ) +{ + bool is_negative = a.amount.value < 0; + uint128_t result( is_negative ? -a.amount.value : a.amount.value ); + if( a.symbol == b.base.symbol ) + { + result = ( result * b.quote.amount.value ) / b.base.amount.value; + return asset( is_negative ? -result.to_uint64() : result.to_uint64(), b.quote.symbol ); + } + else + { + FC_ASSERT( a.symbol == b.quote.symbol, "invalid ${asset} * ${price}", ( "asset", a )( "price", b ) ); + result = ( result * b.base.amount.value ) / b.quote.amount.value; + return asset( is_negative ? -result.to_uint64() : result.to_uint64(), b.base.symbol ); + } +} - price operator / ( const asset& base, const asset& quote ) - { try { - FC_ASSERT( base.symbol != quote.symbol ); - return price{ base, quote }; - } FC_CAPTURE_AND_RETHROW( (base)(quote) ) } +price operator / ( const asset& base, const asset& quote ) +{ try { + return price( base, quote ); +} FC_CAPTURE_AND_RETHROW( (base)(quote) ) } - price price::max( asset_symbol_type base, asset_symbol_type quote ) { return asset( share_type(HIVE_MAX_SATOSHIS), base ) / asset( share_type(1), quote); } - price price::min( asset_symbol_type base, asset_symbol_type quote ) { return asset( 1, base ) / asset( HIVE_MAX_SATOSHIS, quote); } +price price::max( asset_symbol_type base, asset_symbol_type quote ) { return asset( share_type(HIVE_MAX_SATOSHIS), base ) / asset( share_type(1), quote); } +price price::min( asset_symbol_type base, asset_symbol_type quote ) { return asset( 1, base ) / asset( HIVE_MAX_SATOSHIS, quote); } - bool price::is_null() const { return *this == price(); } +bool price::is_null() const { return *this == price(); } - void price::validate() const - { try { - FC_ASSERT( base.amount > share_type(0) ); - FC_ASSERT( quote.amount > share_type(0) ); - FC_ASSERT( base.symbol != quote.symbol ); - } FC_CAPTURE_AND_RETHROW( (base)(quote) ) } +void price::validate() const +{ try { + FC_ASSERT( base.amount > share_type(0) ); + FC_ASSERT( quote.amount > share_type(0) ); + FC_ASSERT( base.symbol != quote.symbol ); +} FC_CAPTURE_AND_RETHROW( (base)(quote) ) } +asset multiply_with_fee( const asset& a, const price& p, uint16_t fee, asset_symbol_type apply_fee_to ) +{ + bool is_negative = a.amount.value < 0; + uint128_t result( is_negative ? -a.amount.value : a.amount.value ); + uint16_t scale_b = HIVE_100_PERCENT, scale_q = HIVE_100_PERCENT; + if( apply_fee_to == p.base.symbol ) + { + scale_b += fee; + } + else + { + FC_ASSERT( apply_fee_to == p.quote.symbol, "Invalid fee symbol ${at} for price ${p}", ( "at", apply_fee_to )( "p", p ) ); + scale_q += fee; + } + + if( a.symbol == p.base.symbol ) + { + result = ( result * p.quote.amount.value * scale_q ) / ( uint128_t( p.base.amount.value ) * scale_b ); + return asset( is_negative ? -result.to_uint64() : result.to_uint64(), p.quote.symbol ); + } + else + { + FC_ASSERT( a.symbol == p.quote.symbol, "invalid ${asset} * ${price}", ( "asset", a )( "price", p ) ); + result = ( result * p.base.amount.value * scale_b ) / ( uint128_t ( p.quote.amount.value ) * scale_q ); + return asset( is_negative ? -result.to_uint64() : result.to_uint64(), p.base.symbol ); + } +} } } // hive::protocol diff --git a/libraries/protocol/get_config.cpp b/libraries/protocol/get_config.cpp deleted file mode 100644 index e33df59d5b..0000000000 --- a/libraries/protocol/get_config.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include -#include -#include -#include -#include - -namespace hive { namespace protocol { - -fc::variant_object get_config( const std::string& treasury_name, const fc::sha256& chain_id ) -{ - fc::mutable_variant_object result; - -#ifdef IS_TEST_NET - result[ "IS_TEST_NET" ] = true; - result["TESTNET_BLOCK_LIMIT"] = TESTNET_BLOCK_LIMIT; -#else - result[ "IS_TEST_NET" ] = false; -#endif -#ifdef HIVE_ENABLE_SMT - result[ "HIVE_ENABLE_SMT" ] = true; - result["SMT_MAX_VOTABLE_ASSETS"] = SMT_MAX_VOTABLE_ASSETS; - result["SMT_VESTING_WITHDRAW_INTERVAL_SECONDS"] = SMT_VESTING_WITHDRAW_INTERVAL_SECONDS; - result["SMT_UPVOTE_LOCKOUT"] = SMT_UPVOTE_LOCKOUT; - result["SMT_EMISSION_MIN_INTERVAL_SECONDS"] = SMT_EMISSION_MIN_INTERVAL_SECONDS; - result["SMT_EMIT_INDEFINITELY"] = SMT_EMIT_INDEFINITELY; - result["SMT_MAX_NOMINAL_VOTES_PER_DAY"] = SMT_MAX_NOMINAL_VOTES_PER_DAY; - result["SMT_MAX_VOTES_PER_REGENERATION"] = SMT_MAX_VOTES_PER_REGENERATION; - result["SMT_DEFAULT_VOTES_PER_REGEN_PERIOD"] = SMT_DEFAULT_VOTES_PER_REGEN_PERIOD; - result["SMT_DEFAULT_PERCENT_CURATION_REWARDS"] = SMT_DEFAULT_PERCENT_CURATION_REWARDS; -#else - result[ "HIVE_ENABLE_SMT" ] = false; -#endif - - result["HBD_SYMBOL"] = HBD_SYMBOL; - result["HIVE_INITIAL_VOTE_POWER_RATE"] = HIVE_INITIAL_VOTE_POWER_RATE; - result["HIVE_REDUCED_VOTE_POWER_RATE"] = HIVE_REDUCED_VOTE_POWER_RATE; - result["HIVE_100_PERCENT"] = HIVE_100_PERCENT; - result["HIVE_1_PERCENT"] = HIVE_1_PERCENT; - result["HIVE_ACCOUNT_RECOVERY_REQUEST_EXPIRATION_PERIOD"] = HIVE_ACCOUNT_RECOVERY_REQUEST_EXPIRATION_PERIOD; - result["HIVE_ACTIVE_CHALLENGE_COOLDOWN"] = HIVE_ACTIVE_CHALLENGE_COOLDOWN; - result["HIVE_ACTIVE_CHALLENGE_FEE"] = HIVE_ACTIVE_CHALLENGE_FEE; - result["HIVE_ADDRESS_PREFIX"] = HIVE_ADDRESS_PREFIX; - result["HIVE_APR_PERCENT_MULTIPLY_PER_BLOCK"] = HIVE_APR_PERCENT_MULTIPLY_PER_BLOCK; - result["HIVE_APR_PERCENT_MULTIPLY_PER_HOUR"] = HIVE_APR_PERCENT_MULTIPLY_PER_HOUR; - result["HIVE_APR_PERCENT_MULTIPLY_PER_ROUND"] = HIVE_APR_PERCENT_MULTIPLY_PER_ROUND; - result["HIVE_APR_PERCENT_SHIFT_PER_BLOCK"] = HIVE_APR_PERCENT_SHIFT_PER_BLOCK; - result["HIVE_APR_PERCENT_SHIFT_PER_HOUR"] = HIVE_APR_PERCENT_SHIFT_PER_HOUR; - result["HIVE_APR_PERCENT_SHIFT_PER_ROUND"] = HIVE_APR_PERCENT_SHIFT_PER_ROUND; - result["HIVE_BANDWIDTH_AVERAGE_WINDOW_SECONDS"] = HIVE_BANDWIDTH_AVERAGE_WINDOW_SECONDS; - result["HIVE_BANDWIDTH_PRECISION"] = HIVE_BANDWIDTH_PRECISION; - result["HIVE_BENEFICIARY_LIMIT"] = HIVE_BENEFICIARY_LIMIT; - result["HIVE_BLOCKCHAIN_PRECISION"] = HIVE_BLOCKCHAIN_PRECISION; - result["HIVE_BLOCKCHAIN_PRECISION_DIGITS"] = HIVE_BLOCKCHAIN_PRECISION_DIGITS; - result["HIVE_BLOCKCHAIN_HARDFORK_VERSION"] = HIVE_BLOCKCHAIN_HARDFORK_VERSION; - result["HIVE_BLOCKCHAIN_VERSION"] = HIVE_BLOCKCHAIN_VERSION; - result["HIVE_BLOCK_INTERVAL"] = HIVE_BLOCK_INTERVAL; - result["HIVE_BLOCKS_PER_DAY"] = HIVE_BLOCKS_PER_DAY; - result["HIVE_BLOCKS_PER_HOUR"] = HIVE_BLOCKS_PER_HOUR; - result["HIVE_BLOCKS_PER_YEAR"] = HIVE_BLOCKS_PER_YEAR; - result["HIVE_CASHOUT_WINDOW_SECONDS"] = HIVE_CASHOUT_WINDOW_SECONDS; - result["HIVE_CASHOUT_WINDOW_SECONDS_PRE_HF12"] = HIVE_CASHOUT_WINDOW_SECONDS_PRE_HF12; - result["HIVE_CASHOUT_WINDOW_SECONDS_PRE_HF17"] = HIVE_CASHOUT_WINDOW_SECONDS_PRE_HF17; - result["HIVE_CHAIN_ID"] = chain_id; - result["HIVE_COMMENT_REWARD_FUND_NAME"] = HIVE_COMMENT_REWARD_FUND_NAME; - result["HIVE_COMMENT_TITLE_LIMIT"] = HIVE_COMMENT_TITLE_LIMIT; - result["HIVE_CONTENT_APR_PERCENT"] = HIVE_CONTENT_APR_PERCENT; - result["HIVE_CONTENT_CONSTANT_HF0"] = HIVE_CONTENT_CONSTANT_HF0; - result["HIVE_CONTENT_CONSTANT_HF21"] = HIVE_CONTENT_CONSTANT_HF21; - result["HIVE_CONTENT_REWARD_PERCENT_HF16"] = HIVE_CONTENT_REWARD_PERCENT_HF16; - result["HIVE_CONTENT_REWARD_PERCENT_HF21"] = HIVE_CONTENT_REWARD_PERCENT_HF21; - result["HIVE_CONVERSION_DELAY"] = HIVE_CONVERSION_DELAY; - result["HIVE_CONVERSION_DELAY_PRE_HF_16"] = HIVE_CONVERSION_DELAY_PRE_HF_16; - result["HIVE_CREATE_ACCOUNT_DELEGATION_RATIO"] = HIVE_CREATE_ACCOUNT_DELEGATION_RATIO; - result["HIVE_CREATE_ACCOUNT_DELEGATION_TIME"] = HIVE_CREATE_ACCOUNT_DELEGATION_TIME; - result["HIVE_CREATE_ACCOUNT_WITH_HIVE_MODIFIER"] = HIVE_CREATE_ACCOUNT_WITH_HIVE_MODIFIER; - result["HIVE_CURATE_APR_PERCENT"] = HIVE_CURATE_APR_PERCENT; - result["HIVE_CUSTOM_OP_DATA_MAX_LENGTH"] = HIVE_CUSTOM_OP_DATA_MAX_LENGTH; - result["HIVE_CUSTOM_OP_ID_MAX_LENGTH"] = HIVE_CUSTOM_OP_ID_MAX_LENGTH; - result["HIVE_DEFAULT_HBD_INTEREST_RATE"] = HIVE_DEFAULT_HBD_INTEREST_RATE; - result["HIVE_DOWNVOTE_POOL_PERCENT_HF21"] = HIVE_DOWNVOTE_POOL_PERCENT_HF21; - result["HIVE_EQUIHASH_K"] = HIVE_EQUIHASH_K; - result["HIVE_EQUIHASH_N"] = HIVE_EQUIHASH_N; - result["HIVE_FEED_HISTORY_WINDOW"] = HIVE_FEED_HISTORY_WINDOW; - result["HIVE_FEED_HISTORY_WINDOW_PRE_HF_16"] = HIVE_FEED_HISTORY_WINDOW_PRE_HF_16; - result["HIVE_FEED_INTERVAL_BLOCKS"] = HIVE_FEED_INTERVAL_BLOCKS; - result["HIVE_GENESIS_TIME"] = HIVE_GENESIS_TIME; - result["HIVE_HARDFORK_REQUIRED_WITNESSES"] = HIVE_HARDFORK_REQUIRED_WITNESSES; - result["HIVE_HF21_CONVERGENT_LINEAR_RECENT_CLAIMS"] = HIVE_HF21_CONVERGENT_LINEAR_RECENT_CLAIMS; - result["HIVE_INFLATION_NARROWING_PERIOD"] = HIVE_INFLATION_NARROWING_PERIOD; - result["HIVE_INFLATION_RATE_START_PERCENT"] = HIVE_INFLATION_RATE_START_PERCENT; - result["HIVE_INFLATION_RATE_STOP_PERCENT"] = HIVE_INFLATION_RATE_STOP_PERCENT; - result["HIVE_INIT_MINER_NAME"] = HIVE_INIT_MINER_NAME; - result["HIVE_INIT_PUBLIC_KEY_STR"] = HIVE_INIT_PUBLIC_KEY_STR; - -#if 0 - // do not expose private key, period. - // we need this line present but inactivated so CI check for all constants in config.hpp doesn't complain. - result["HIVE_INIT_PRIVATE_KEY"] = HIVE_INIT_PRIVATE_KEY; -#endif - result["HIVE_INIT_SUPPLY"] = HIVE_INIT_SUPPLY; - result["HIVE_HBD_INIT_SUPPLY"] = HIVE_HBD_INIT_SUPPLY; - result["HIVE_INIT_TIME"] = HIVE_INIT_TIME; - result["HIVE_IRREVERSIBLE_THRESHOLD"] = HIVE_IRREVERSIBLE_THRESHOLD; - result["HIVE_LIQUIDITY_APR_PERCENT"] = HIVE_LIQUIDITY_APR_PERCENT; - result["HIVE_LIQUIDITY_REWARD_BLOCKS"] = HIVE_LIQUIDITY_REWARD_BLOCKS; - result["HIVE_LIQUIDITY_REWARD_PERIOD_SEC"] = HIVE_LIQUIDITY_REWARD_PERIOD_SEC; - result["HIVE_LIQUIDITY_TIMEOUT_SEC"] = HIVE_LIQUIDITY_TIMEOUT_SEC; - result["HIVE_MAX_ACCOUNT_CREATION_FEE"] = HIVE_MAX_ACCOUNT_CREATION_FEE; - result["HIVE_MAX_ACCOUNT_NAME_LENGTH"] = HIVE_MAX_ACCOUNT_NAME_LENGTH; - result["HIVE_MAX_ACCOUNT_WITNESS_VOTES"] = HIVE_MAX_ACCOUNT_WITNESS_VOTES; - result["HIVE_MAX_ASSET_WHITELIST_AUTHORITIES"] = HIVE_MAX_ASSET_WHITELIST_AUTHORITIES; - result["HIVE_MAX_AUTHORITY_MEMBERSHIP"] = HIVE_MAX_AUTHORITY_MEMBERSHIP; - result["HIVE_MAX_BLOCK_SIZE"] = HIVE_MAX_BLOCK_SIZE; - result["HIVE_SOFT_MAX_BLOCK_SIZE"] = HIVE_SOFT_MAX_BLOCK_SIZE; - result["HIVE_MAX_CASHOUT_WINDOW_SECONDS"] = HIVE_MAX_CASHOUT_WINDOW_SECONDS; - result["HIVE_MAX_COMMENT_DEPTH"] = HIVE_MAX_COMMENT_DEPTH; - result["HIVE_MAX_COMMENT_DEPTH_PRE_HF17"] = HIVE_MAX_COMMENT_DEPTH_PRE_HF17; - result["HIVE_MAX_FEED_AGE_SECONDS"] = HIVE_MAX_FEED_AGE_SECONDS; - result["HIVE_MAX_INSTANCE_ID"] = HIVE_MAX_INSTANCE_ID; - result["HIVE_MAX_MEMO_SIZE"] = HIVE_MAX_MEMO_SIZE; - result["HIVE_MAX_WITNESSES"] = HIVE_MAX_WITNESSES; - result["HIVE_MAX_MINER_WITNESSES_HF0"] = HIVE_MAX_MINER_WITNESSES_HF0; - result["HIVE_MAX_MINER_WITNESSES_HF17"] = HIVE_MAX_MINER_WITNESSES_HF17; - result["HIVE_MAX_PERMLINK_LENGTH"] = HIVE_MAX_PERMLINK_LENGTH; - result["HIVE_MAX_PROXY_RECURSION_DEPTH"] = HIVE_MAX_PROXY_RECURSION_DEPTH; - result["HIVE_MAX_RATION_DECAY_RATE"] = HIVE_MAX_RATION_DECAY_RATE; - result["HIVE_MAX_RESERVE_RATIO"] = HIVE_MAX_RESERVE_RATIO; - result["HIVE_MAX_RUNNER_WITNESSES_HF0"] = HIVE_MAX_RUNNER_WITNESSES_HF0; - result["HIVE_MAX_RUNNER_WITNESSES_HF17"] = HIVE_MAX_RUNNER_WITNESSES_HF17; - result["HIVE_MAX_SATOSHIS"] = HIVE_MAX_SATOSHIS; - result["HIVE_MAX_SHARE_SUPPLY"] = HIVE_MAX_SHARE_SUPPLY; - result["HIVE_MAX_SIG_CHECK_DEPTH"] = HIVE_MAX_SIG_CHECK_DEPTH; - result["HIVE_MAX_SIG_CHECK_ACCOUNTS"] = HIVE_MAX_SIG_CHECK_ACCOUNTS; - result["HIVE_MAX_TIME_UNTIL_EXPIRATION"] = HIVE_MAX_TIME_UNTIL_EXPIRATION; - result["HIVE_MAX_TRANSACTION_SIZE"] = HIVE_MAX_TRANSACTION_SIZE; - result["HIVE_MAX_UNDO_HISTORY"] = HIVE_MAX_UNDO_HISTORY; - result["HIVE_MAX_URL_LENGTH"] = HIVE_MAX_URL_LENGTH; - result["HIVE_MAX_VOTE_CHANGES"] = HIVE_MAX_VOTE_CHANGES; - result["HIVE_MAX_VOTED_WITNESSES_HF0"] = HIVE_MAX_VOTED_WITNESSES_HF0; - result["HIVE_MAX_VOTED_WITNESSES_HF17"] = HIVE_MAX_VOTED_WITNESSES_HF17; - result["HIVE_MAX_WITHDRAW_ROUTES"] = HIVE_MAX_WITHDRAW_ROUTES; - result["HIVE_MAX_PENDING_TRANSFERS"] = HIVE_MAX_PENDING_TRANSFERS; - result["HIVE_MAX_WITNESS_URL_LENGTH"] = HIVE_MAX_WITNESS_URL_LENGTH; - result["HIVE_MIN_ACCOUNT_CREATION_FEE"] = HIVE_MIN_ACCOUNT_CREATION_FEE; - result["HIVE_MIN_ACCOUNT_NAME_LENGTH"] = HIVE_MIN_ACCOUNT_NAME_LENGTH; - result["HIVE_MIN_BLOCK_SIZE_LIMIT"] = HIVE_MIN_BLOCK_SIZE_LIMIT; - result["HIVE_MIN_BLOCK_SIZE"] = HIVE_MIN_BLOCK_SIZE; - result["HIVE_MIN_CONTENT_REWARD"] = HIVE_MIN_CONTENT_REWARD; - result["HIVE_MIN_CURATE_REWARD"] = HIVE_MIN_CURATE_REWARD; - result["HIVE_MIN_PERMLINK_LENGTH"] = HIVE_MIN_PERMLINK_LENGTH; - result["HIVE_MIN_REPLY_INTERVAL"] = HIVE_MIN_REPLY_INTERVAL; - result["HIVE_MIN_REPLY_INTERVAL_HF20"] = HIVE_MIN_REPLY_INTERVAL_HF20; - result["HIVE_MIN_ROOT_COMMENT_INTERVAL"] = HIVE_MIN_ROOT_COMMENT_INTERVAL; - result["HIVE_MIN_COMMENT_EDIT_INTERVAL"] = HIVE_MIN_COMMENT_EDIT_INTERVAL; - result["HIVE_MIN_VOTE_INTERVAL_SEC"] = HIVE_MIN_VOTE_INTERVAL_SEC; - result["HIVE_MINER_ACCOUNT"] = HIVE_MINER_ACCOUNT; - result["HIVE_MINER_PAY_PERCENT"] = HIVE_MINER_PAY_PERCENT; - result["HIVE_MIN_FEEDS"] = HIVE_MIN_FEEDS; - result["HIVE_MINING_REWARD"] = HIVE_MINING_REWARD; - result["HIVE_MINING_TIME"] = HIVE_MINING_TIME; - result["HIVE_MIN_LIQUIDITY_REWARD"] = HIVE_MIN_LIQUIDITY_REWARD; - result["HIVE_MIN_LIQUIDITY_REWARD_PERIOD_SEC"] = HIVE_MIN_LIQUIDITY_REWARD_PERIOD_SEC; - result["HIVE_MIN_PAYOUT_HBD"] = HIVE_MIN_PAYOUT_HBD; - result["HIVE_MIN_POW_REWARD"] = HIVE_MIN_POW_REWARD; - result["HIVE_MIN_PRODUCER_REWARD"] = HIVE_MIN_PRODUCER_REWARD; - result["HIVE_MIN_TRANSACTION_EXPIRATION_LIMIT"] = HIVE_MIN_TRANSACTION_EXPIRATION_LIMIT; - result["HIVE_MIN_TRANSACTION_SIZE_LIMIT"] = HIVE_MIN_TRANSACTION_SIZE_LIMIT; - result["HIVE_MIN_UNDO_HISTORY"] = HIVE_MIN_UNDO_HISTORY; - result["HIVE_NULL_ACCOUNT"] = HIVE_NULL_ACCOUNT; - result["HIVE_NUM_INIT_MINERS"] = HIVE_NUM_INIT_MINERS; - result["HIVE_OWNER_AUTH_HISTORY_TRACKING_START_BLOCK_NUM"] = HIVE_OWNER_AUTH_HISTORY_TRACKING_START_BLOCK_NUM; - result["HIVE_OWNER_AUTH_RECOVERY_PERIOD"] = HIVE_OWNER_AUTH_RECOVERY_PERIOD; - result["HIVE_OWNER_CHALLENGE_COOLDOWN"] = HIVE_OWNER_CHALLENGE_COOLDOWN; - result["HIVE_OWNER_CHALLENGE_FEE"] = HIVE_OWNER_CHALLENGE_FEE; - result["HIVE_OWNER_UPDATE_LIMIT"] = HIVE_OWNER_UPDATE_LIMIT; - result["HIVE_POST_AVERAGE_WINDOW"] = HIVE_POST_AVERAGE_WINDOW; - result["HIVE_POST_REWARD_FUND_NAME"] = HIVE_POST_REWARD_FUND_NAME; - result["HIVE_POST_WEIGHT_CONSTANT"] = HIVE_POST_WEIGHT_CONSTANT; - result["HIVE_POW_APR_PERCENT"] = HIVE_POW_APR_PERCENT; - result["HIVE_PRODUCER_APR_PERCENT"] = HIVE_PRODUCER_APR_PERCENT; - result["HIVE_PROXY_TO_SELF_ACCOUNT"] = HIVE_PROXY_TO_SELF_ACCOUNT; - result["HIVE_HBD_INTEREST_COMPOUND_INTERVAL_SEC"] = HIVE_HBD_INTEREST_COMPOUND_INTERVAL_SEC; - result["HIVE_SECONDS_PER_YEAR"] = HIVE_SECONDS_PER_YEAR; - result["HIVE_PROPOSAL_FUND_PERCENT_HF0"] = HIVE_PROPOSAL_FUND_PERCENT_HF0; - result["HIVE_PROPOSAL_FUND_PERCENT_HF21"] = HIVE_PROPOSAL_FUND_PERCENT_HF21; - result["HIVE_RECENT_RSHARES_DECAY_TIME_HF19"] = HIVE_RECENT_RSHARES_DECAY_TIME_HF19; - result["HIVE_RECENT_RSHARES_DECAY_TIME_HF17"] = HIVE_RECENT_RSHARES_DECAY_TIME_HF17; - result["HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF6"] = HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF6; - result["HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF20"] = HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF20; - result["HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF21"] = HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF21; - result["HIVE_ROOT_POST_PARENT"] = HIVE_ROOT_POST_PARENT; - result["HIVE_SAVINGS_WITHDRAW_REQUEST_LIMIT"] = HIVE_SAVINGS_WITHDRAW_REQUEST_LIMIT; - result["HIVE_SAVINGS_WITHDRAW_TIME"] = HIVE_SAVINGS_WITHDRAW_TIME; - result["HIVE_HBD_START_PERCENT_HF14"] = HIVE_HBD_START_PERCENT_HF14; - result["HIVE_HBD_START_PERCENT_HF20"] = HIVE_HBD_START_PERCENT_HF20; - result["HIVE_HBD_STOP_PERCENT_HF14"] = HIVE_HBD_STOP_PERCENT_HF14; - result["HIVE_HBD_STOP_PERCENT_HF20"] = HIVE_HBD_STOP_PERCENT_HF20; - result["HIVE_SECOND_CASHOUT_WINDOW"] = HIVE_SECOND_CASHOUT_WINDOW; - result["HIVE_SOFT_MAX_COMMENT_DEPTH"] = HIVE_SOFT_MAX_COMMENT_DEPTH; - result["HIVE_START_MINER_VOTING_BLOCK"] = HIVE_START_MINER_VOTING_BLOCK; - result["HIVE_START_VESTING_BLOCK"] = HIVE_START_VESTING_BLOCK; - result["HIVE_TEMP_ACCOUNT"] = HIVE_TEMP_ACCOUNT; - result["HIVE_UPVOTE_LOCKOUT_HF7"] = HIVE_UPVOTE_LOCKOUT_HF7; - result["HIVE_UPVOTE_LOCKOUT_HF17"] = HIVE_UPVOTE_LOCKOUT_HF17; - result["HIVE_UPVOTE_LOCKOUT_SECONDS"] = HIVE_UPVOTE_LOCKOUT_SECONDS; - result["HIVE_VESTING_FUND_PERCENT_HF16"] = HIVE_VESTING_FUND_PERCENT_HF16; - result["HIVE_VESTING_WITHDRAW_INTERVALS"] = HIVE_VESTING_WITHDRAW_INTERVALS; - result["HIVE_VESTING_WITHDRAW_INTERVALS_PRE_HF_16"] = HIVE_VESTING_WITHDRAW_INTERVALS_PRE_HF_16; - result["HIVE_VESTING_WITHDRAW_INTERVAL_SECONDS"] = HIVE_VESTING_WITHDRAW_INTERVAL_SECONDS; - result["HIVE_VOTE_DUST_THRESHOLD"] = HIVE_VOTE_DUST_THRESHOLD; - result["HIVE_VOTING_MANA_REGENERATION_SECONDS"] = HIVE_VOTING_MANA_REGENERATION_SECONDS; - result["HIVE_SYMBOL"] = HIVE_SYMBOL; - result["VESTS_SYMBOL"] = VESTS_SYMBOL; - result["HIVE_VIRTUAL_SCHEDULE_LAP_LENGTH"] = HIVE_VIRTUAL_SCHEDULE_LAP_LENGTH; - result["HIVE_VIRTUAL_SCHEDULE_LAP_LENGTH2"] = HIVE_VIRTUAL_SCHEDULE_LAP_LENGTH2; - result["HIVE_MAX_LIMIT_ORDER_EXPIRATION"] = HIVE_MAX_LIMIT_ORDER_EXPIRATION; - result["HIVE_DELEGATION_RETURN_PERIOD_HF0"] = HIVE_DELEGATION_RETURN_PERIOD_HF0; - result["HIVE_DELEGATION_RETURN_PERIOD_HF20"] = HIVE_DELEGATION_RETURN_PERIOD_HF20; - result["HIVE_RD_MIN_DECAY_BITS"] = HIVE_RD_MIN_DECAY_BITS; - result["HIVE_RD_MAX_DECAY_BITS"] = HIVE_RD_MAX_DECAY_BITS; - result["HIVE_RD_DECAY_DENOM_SHIFT"] = HIVE_RD_DECAY_DENOM_SHIFT; - result["HIVE_RD_MAX_POOL_BITS"] = HIVE_RD_MAX_POOL_BITS; - result["HIVE_RD_MAX_BUDGET_1"] = HIVE_RD_MAX_BUDGET_1; - result["HIVE_RD_MAX_BUDGET_2"] = HIVE_RD_MAX_BUDGET_2; - result["HIVE_RD_MAX_BUDGET_3"] = HIVE_RD_MAX_BUDGET_3; - result["HIVE_RD_MAX_BUDGET"] = HIVE_RD_MAX_BUDGET; - result["HIVE_RD_MIN_DECAY"] = HIVE_RD_MIN_DECAY; - result["HIVE_RD_MIN_BUDGET"] = HIVE_RD_MIN_BUDGET; - result["HIVE_RD_MAX_DECAY"] = HIVE_RD_MAX_DECAY; - result["HIVE_ACCOUNT_SUBSIDY_PRECISION"] = HIVE_ACCOUNT_SUBSIDY_PRECISION; - result["HIVE_WITNESS_SUBSIDY_BUDGET_PERCENT"] = HIVE_WITNESS_SUBSIDY_BUDGET_PERCENT; - result["HIVE_WITNESS_SUBSIDY_DECAY_PERCENT"] = HIVE_WITNESS_SUBSIDY_DECAY_PERCENT; - result["HIVE_DEFAULT_ACCOUNT_SUBSIDY_DECAY"] = HIVE_DEFAULT_ACCOUNT_SUBSIDY_DECAY; - result["HIVE_DEFAULT_ACCOUNT_SUBSIDY_BUDGET"] = HIVE_DEFAULT_ACCOUNT_SUBSIDY_BUDGET; - result["HIVE_DECAY_BACKSTOP_PERCENT"] = HIVE_DECAY_BACKSTOP_PERCENT; - result["HIVE_BLOCK_GENERATION_POSTPONED_TX_LIMIT"] = HIVE_BLOCK_GENERATION_POSTPONED_TX_LIMIT; - result["HIVE_PENDING_TRANSACTION_EXECUTION_LIMIT"] = HIVE_PENDING_TRANSACTION_EXECUTION_LIMIT; - result["HIVE_TREASURY_ACCOUNT"] = treasury_name; - result["HIVE_TREASURY_FEE"] = HIVE_TREASURY_FEE; - result["HIVE_PROPOSAL_MAINTENANCE_PERIOD"] = HIVE_PROPOSAL_MAINTENANCE_PERIOD; - result["HIVE_PROPOSAL_MAINTENANCE_CLEANUP"] = HIVE_PROPOSAL_MAINTENANCE_CLEANUP; - result["HIVE_PROPOSAL_SUBJECT_MAX_LENGTH"] = HIVE_PROPOSAL_SUBJECT_MAX_LENGTH; - result["HIVE_PROPOSAL_MAX_IDS_NUMBER"] = HIVE_PROPOSAL_MAX_IDS_NUMBER; - - return result; -} - -} } // hive::protocol diff --git a/libraries/protocol/get_config.d/generate_get_config.cmake b/libraries/protocol/get_config.d/generate_get_config.cmake new file mode 100644 index 0000000000..f74e3d66d2 --- /dev/null +++ b/libraries/protocol/get_config.d/generate_get_config.cmake @@ -0,0 +1,66 @@ +macro(generate_get_config path_to_config_hpp get_config_cpp_in get_config_cpp_out) + + file(COPY ${path_to_config_hpp} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + set(path_to_new_config_hpp "${CMAKE_CURRENT_BINARY_DIR}/config.hpp") + + # removing includes and pragmas + file(STRINGS ${path_to_new_config_hpp} file_content ) + list(FILTER file_content EXCLUDE REGEX "#(include|pragma)" ) + set(prepared_get_content) + foreach(x ${file_content}) + list( APPEND prepared_get_content "${x}\n" ) + endforeach() + + set(_OUT_FILE "${path_to_new_config_hpp}.pregen.preprocessed.hpp" ) + set(OUT_FILE "${path_to_new_config_hpp}.preprocessed" ) + + # rewriting gile + file(WRITE ${_OUT_FILE} ${prepared_get_content} ) + + # setup compiler flags for dry run + set(local_compiler_flags "-E") + list(APPEND local_compiler_flags "-fdirectives-only" "${_OUT_FILE}" "-o" "${OUT_FILE}") + if( BUILD_HIVE_TESTNET ) + list(APPEND local_compiler_flags "-DIS_TEST_NET") + endif() + if( ENABLE_SMT_SUPPORT ) + list(APPEND local_compiler_flags "-DHIVE_ENABLE_SMT") + endif() + + # using of compiler dry run to preprocess config.hpp + message("running c++ compiler wit flags: ${local_compiler_flags}") + execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${local_compiler_flags}) + + # trim additional output from comiler till the comment from header file + file(STRINGS ${OUT_FILE} config_hpp_content ) + set(preamula_on 1) + set(list_of_new_lines) + foreach(line in ${config_hpp_content}) + if( ${preamula_on} EQUAL 1 ) + string(FIND "${line}" "Steemit" steemit_found ) + if( ${steemit_found} GREATER_EQUAL 0 ) + set(preamula_on 0) + else() + continue() + endif() + endif() + + # parse defines + if(${line} MATCHES "^([ ]*#define)") + string(REGEX REPLACE "#define ([A-Z0-9_]+) .*" "\\1" UNSAFE_VALUE "${line}") + string(STRIP ${UNSAFE_VALUE} VALUE) + list( APPEND list_of_new_lines " result[\"${VALUE}\"] = ${VALUE}\;\n" ) + endif() + endforeach() + + # convert list to single varriable + set(CONFIG_HPP) + foreach(x ${list_of_new_lines}) + set(CONFIG_HPP "${CONFIG_HPP}${x}") + endforeach() + + configure_file(${get_config_cpp_in} ${get_config_cpp_out} ) + message("get_config.cpp has been generated in `${get_config_cpp_out}`") + set(GET_CONFIG_CPP ${get_config_cpp_out}) + +endmacro() \ No newline at end of file diff --git a/libraries/protocol/get_config.d/get_config.cpp.in b/libraries/protocol/get_config.d/get_config.cpp.in new file mode 100644 index 0000000000..e74e37a4b8 --- /dev/null +++ b/libraries/protocol/get_config.d/get_config.cpp.in @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + +namespace hive { namespace protocol { + +fc::variant_object get_config( const std::string& treasury_name, const fc::sha256& chain_id ) +{ + fc::mutable_variant_object result; + result["HIVE_CHAIN_ID"] = chain_id; + result["HIVE_TREASURY_ACCOUNT"] = treasury_name; + +#ifdef IS_TEST_NET + result["IS_TEST_NET"] = true; +#else + result["IS_TEST_NET"] = false; +#endif + +#ifdef HIVE_ENABLE_SMT + result["HIVE_ENABLE_SMT"] = true; +#else + result["HIVE_ENABLE_SMT"] = false; +#endif + +@CONFIG_HPP@ + + return result; +} + +} } // hive::protocol diff --git a/libraries/protocol/hardfork.d/0-preamble.hf b/libraries/protocol/hardfork.d/0-preamble.hf index 23854d0f7b..456fa99820 100644 --- a/libraries/protocol/hardfork.d/0-preamble.hf +++ b/libraries/protocol/hardfork.d/0-preamble.hf @@ -12,8 +12,8 @@ #include #include -#ifdef IS_TEST_NET -#define HIVE_NUM_HARDFORKS 25 +#if defined(IS_TEST_NET) && defined(HIVE_ENABLE_SMT) +#define HIVE_NUM_HARDFORKS 26 #else -#define HIVE_NUM_HARDFORKS 24 +#define HIVE_NUM_HARDFORKS 25 #endif diff --git a/libraries/protocol/hardfork.d/0_23.hf b/libraries/protocol/hardfork.d/0_23.hf index b38bec81f5..b32dc08ca1 100644 --- a/libraries/protocol/hardfork.d/0_23.hf +++ b/libraries/protocol/hardfork.d/0_23.hf @@ -1,9 +1,7 @@ #ifndef HIVE_HARDFORK_0_23 #define HIVE_HARDFORK_0_23 23 -long hf23_time(); - -#define HIVE_HARDFORK_0_23_TIME hf23_time() +#define HIVE_HARDFORK_0_23_TIME 1584712800 // Friday, 20 March 2020 14:00:00 GMT #define HIVE_HARDFORK_0_23_VERSION hardfork_version( 0, 23 ) diff --git a/libraries/protocol/hardfork.d/0_24.hf b/libraries/protocol/hardfork.d/0_24.hf index dc198f7520..a28f96c3dc 100644 --- a/libraries/protocol/hardfork.d/0_24.hf +++ b/libraries/protocol/hardfork.d/0_24.hf @@ -2,9 +2,11 @@ #define HIVE_HARDFORK_1_24 24 #define HIVE_TREASURY_RENAME_HARDFORK HIVE_HARDFORK_1_24 -long hf24_time(); - -#define HIVE_HARDFORK_1_24_TIME hf24_time() +#ifdef IS_TEST_NET + #define HIVE_HARDFORK_1_24_TIME 1588334400 // Friday, 1 May 2020 12:00:00 GMT +#else + #define HIVE_HARDFORK_1_24_TIME 1601992800 // Tuesday, 06-Oct-2020 14:00:00 UTC +#endif #define HIVE_HARDFORK_1_24_VERSION hardfork_version( 1, 24 ) diff --git a/libraries/protocol/hardfork.d/0_25.hf b/libraries/protocol/hardfork.d/0_25.hf index 69bc46d1e9..29c9e4cd8e 100644 --- a/libraries/protocol/hardfork.d/0_25.hf +++ b/libraries/protocol/hardfork.d/0_25.hf @@ -1,9 +1,16 @@ #ifndef HIVE_HARDFORK_1_25 #define HIVE_HARDFORK_1_25 25 -#define HIVE_SMT_HARDFORK HIVE_HARDFORK_1_25 -#define HIVE_HARDFORK_1_25_TIME 1640952000 // Thursday, 31 December 2021 12:00:00 GMT +long next_hf_time(); + +#define HIVE_HARDFORK_1_25_TIME next_hf_time() #define HIVE_HARDFORK_1_25_VERSION hardfork_version( 1, 25 ) +#define HIVE_HARDFORK_1_25_MAX_OLD_GOVERNANCE_VOTE_EXPIRE_SHIFT (fc::microseconds(HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD.count()/2)) +#define HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP (fc::time_point_sec(HIVE_HARDFORK_1_25_TIME) + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD) + +//hard limit on HBD no longer counts treasury balance (issue #140) +#define HIVE_HARDFORK_1_25_HBD_HARD_CAP (HIVE_HARDFORK_1_25) + #endif diff --git a/libraries/protocol/hardfork.d/0_26.hf b/libraries/protocol/hardfork.d/0_26.hf new file mode 100644 index 0000000000..f079b5f9c4 --- /dev/null +++ b/libraries/protocol/hardfork.d/0_26.hf @@ -0,0 +1,9 @@ +#ifndef HIVE_HARDFORK_1_26 +#define HIVE_HARDFORK_1_26 26 +#define HIVE_SMT_HARDFORK HIVE_HARDFORK_1_26 + +#define HIVE_HARDFORK_1_26_TIME 1640952000 // Thursday, 31 December 2021 12:00:00 GMT + +#define HIVE_HARDFORK_1_26_VERSION hardfork_version( 1, 26 ) + +#endif diff --git a/libraries/protocol/hive_operations.cpp b/libraries/protocol/hive_operations.cpp index 0c89f2054a..f1a0c7a623 100644 --- a/libraries/protocol/hive_operations.cpp +++ b/libraries/protocol/hive_operations.cpp @@ -128,7 +128,7 @@ namespace hive { namespace protocol { FC_ASSERT( beneficiaries.size(), "Must specify at least one beneficiary" ); FC_ASSERT( beneficiaries.size() < HIVE_BENEFICIARY_LIMIT, - "Cannot specify more than ${max} beneficiaries.", ("max", HIVE_BENEFICIARY_LIMIT - 1) ); // Require size serializtion fits in one byte. + "Cannot specify more than ${max} beneficiaries.", ("max", HIVE_BENEFICIARY_LIMIT - 1) ); // Require size serialization fits in one byte. validate_account_name( beneficiaries[0].account ); FC_ASSERT( beneficiaries[0].weight <= HIVE_100_PERCENT, "Cannot allocate more than 100% of rewards to one account" ); @@ -141,7 +141,7 @@ namespace hive { namespace protocol { FC_ASSERT( beneficiaries[i].weight <= HIVE_100_PERCENT, "Cannot allocate more than 100% of rewards to one account" ); sum += beneficiaries[i].weight; FC_ASSERT( sum <= HIVE_100_PERCENT, "Cannot allocate more than 100% of rewards to a comment" ); // Have to check incrementally to avoid overflow - FC_ASSERT( beneficiaries[i - 1] < beneficiaries[i], "Benficiaries must be specified in sorted order (account ascending)" ); + FC_ASSERT( beneficiaries[i - 1] < beneficiaries[i], "Beneficiaries must be specified in sorted order (account ascending)" ); } } @@ -264,7 +264,7 @@ namespace hive { namespace protocol { itr = props.find( "maximum_block_size" ); if( itr != props.end() ) { - uint32_t maximum_block_size; + uint32_t maximum_block_size = 0u; fc::raw::unpack_from_vector( itr->second, maximum_block_size ); FC_ASSERT( maximum_block_size >= HIVE_MIN_BLOCK_SIZE_LIMIT, "maximum_block_size smaller than minimum max block size" ); } @@ -275,7 +275,7 @@ namespace hive { namespace protocol { if( itr != props.end() ) { - uint16_t hbd_interest_rate; + uint16_t hbd_interest_rate = 0u; fc::raw::unpack_from_vector( itr->second, hbd_interest_rate ); FC_ASSERT( hbd_interest_rate >= 0, "hbd_interest_rate must be positive" ); FC_ASSERT( hbd_interest_rate <= HIVE_100_PERCENT, "hbd_interest_rate must not exceed 100%" ); @@ -316,7 +316,7 @@ namespace hive { namespace protocol { itr = props.find( "account_subsidy_budget" ); if( itr != props.end() ) { - int32_t account_subsidy_budget; + int32_t account_subsidy_budget = 0u; fc::raw::unpack_from_vector( itr->second, account_subsidy_budget ); // Checks that the value can be deserialized FC_ASSERT( account_subsidy_budget >= HIVE_RD_MIN_BUDGET, "Budget must be at least ${n}", ("n", HIVE_RD_MIN_BUDGET) ); FC_ASSERT( account_subsidy_budget <= HIVE_RD_MAX_BUDGET, "Budget must be at most ${n}", ("n", HIVE_RD_MAX_BUDGET) ); @@ -325,7 +325,7 @@ namespace hive { namespace protocol { itr = props.find( "account_subsidy_decay" ); if( itr != props.end() ) { - uint32_t account_subsidy_decay; + uint32_t account_subsidy_decay = 0u; fc::raw::unpack_from_vector( itr->second, account_subsidy_decay ); // Checks that the value can be deserialized FC_ASSERT( account_subsidy_decay >= HIVE_RD_MIN_DECAY, "Decay must be at least ${n}", ("n", HIVE_RD_MIN_DECAY) ); FC_ASSERT( account_subsidy_decay <= HIVE_RD_MAX_DECAY, "Decay must be at most ${n}", ("n", HIVE_RD_MAX_DECAY) ); @@ -341,7 +341,7 @@ namespace hive { namespace protocol { void account_witness_proxy_operation::validate() const { validate_account_name( account ); - if( proxy.size() ) + if( !is_clearing_proxy() ) validate_account_name( proxy ); FC_ASSERT( proxy != account, "Cannot proxy to self" ); } @@ -554,6 +554,14 @@ namespace hive { namespace protocol { FC_ASSERT( amount.amount > 0, "Must convert some HBD" ); } + void collateralized_convert_operation::validate()const + { + validate_account_name( owner ); + /// only allow conversion from HIVE to HBD (at least for now) + FC_ASSERT( is_asset_type( amount, HIVE_SYMBOL ), "Can only convert HIVE to HBD" ); + FC_ASSERT( amount.amount > 0, "Must convert some HIVE" ); + } + void report_over_production_operation::validate()const { validate_account_name( reporter ); @@ -692,8 +700,8 @@ namespace hive { namespace protocol { FC_ASSERT( is_asset_type( reward_hive, HIVE_SYMBOL ), "Reward HIVE must be expressed in HIVE" ); FC_ASSERT( is_asset_type( reward_hbd, HBD_SYMBOL ), "Reward HBD must be expressed in HBD" ); FC_ASSERT( is_asset_type( reward_vests, VESTS_SYMBOL ), "Reward VESTS must be expressed in VESTS" ); - FC_ASSERT( reward_hive.amount >= 0, "Cannot claim a negative amount" ); FC_ASSERT( reward_hbd.amount >= 0, "Cannot claim a negative amount" ); + FC_ASSERT( reward_hive.amount >= 0, "Cannot claim a negative amount" ); FC_ASSERT( reward_vests.amount >= 0, "Cannot claim a negative amount" ); FC_ASSERT( reward_hive.amount > 0 || reward_hbd.amount > 0 || reward_vests.amount > 0, "Must claim something." ); } @@ -727,4 +735,19 @@ namespace hive { namespace protocol { FC_ASSERT( vesting_shares >= asset( 0, VESTS_SYMBOL ), "Delegation cannot be negative" ); } + void recurrent_transfer_operation::validate()const + { try { + validate_account_name( from ); + validate_account_name( to ); + FC_ASSERT( amount.symbol.is_vesting() == false, "Transfer of vesting is not allowed." ); + FC_ASSERT( amount.amount >= 0, "Cannot transfer a negative amount (aka: stealing)" ); + FC_ASSERT( recurrence >= HIVE_MIN_RECURRENT_TRANSFERS_RECURRENCE, "Cannot set a transfer recurrence that is less than ${recurrence} hours", ("recurrence", HIVE_MIN_RECURRENT_TRANSFERS_RECURRENCE) ); + FC_ASSERT( memo.size() < HIVE_MAX_MEMO_SIZE, "Memo is too large" ); + FC_ASSERT( fc::is_utf8( memo ), "Memo is not UTF8" ); + FC_ASSERT( from != to, "Cannot set a transfer to yourself" ); + FC_ASSERT(executions >= 2, "Executions must be at least 2, if you set executions to 1 the recurrent transfer will execute immediately and delete itself. You should use a normal transfer operation"); + FC_ASSERT( fc::hours(recurrence * executions).to_seconds() < fc::days(HIVE_MAX_RECURRENT_TRANSFER_END_DATE).to_seconds(), "Cannot set a transfer that would last for longer than ${days} days", ("days", HIVE_MAX_RECURRENT_TRANSFER_END_DATE) ); + } FC_CAPTURE_AND_RETHROW( (*this) ) + } + } } // hive::protocol diff --git a/libraries/protocol/include/hive/protocol/asset.hpp b/libraries/protocol/include/hive/protocol/asset.hpp index f3a3c16d1a..1037760e5a 100644 --- a/libraries/protocol/include/hive/protocol/asset.hpp +++ b/libraries/protocol/include/hive/protocol/asset.hpp @@ -5,16 +5,23 @@ namespace hive { namespace protocol { + template< uint32_t _SYMBOL > + struct tiny_asset; + + using HBD_asset = tiny_asset< HIVE_ASSET_NUM_HBD >; + using HIVE_asset = tiny_asset< HIVE_ASSET_NUM_HIVE >; + using VEST_asset = tiny_asset< HIVE_ASSET_NUM_VESTS >; + struct asset { asset( const asset& _asset, asset_symbol_type id ) - :amount( _asset.amount ),symbol(id){} + : amount( _asset.amount ), symbol( id ) {} asset( share_type a, asset_symbol_type id ) - :amount(a),symbol(id){} + : amount( a ), symbol( id ) {} asset() - :amount(0),symbol(HIVE_SYMBOL){} + : amount( 0 ), symbol( HIVE_SYMBOL ) {} share_type amount; asset_symbol_type symbol; @@ -34,12 +41,14 @@ namespace hive { namespace protocol { amount -= o.amount; return *this; } + asset operator -()const { return asset( -amount, symbol ); } friend bool operator == ( const asset& a, const asset& b ) { return std::tie( a.symbol, a.amount ) == std::tie( b.symbol, b.amount ); } + friend bool operator < ( const asset& a, const asset& b ) { FC_ASSERT( a.symbol == b.symbol ); @@ -75,6 +84,7 @@ namespace hive { namespace protocol { FC_ASSERT( a.symbol == b.symbol ); return asset( a.amount - b.amount, a.symbol ); } + friend asset operator + ( const asset& a, const asset& b ) { FC_ASSERT( a.symbol == b.symbol ); @@ -113,8 +123,8 @@ namespace hive { namespace protocol { */ explicit price(const asset& base, const asset& quote) : base(base),quote(quote) { - /// Call validate to verify passed arguments. \warning It throws on error. - validate(); + /// Call validate to verify passed arguments. \warning It throws on error. + validate(); } /** Default constructor is needed because of fc::variant::as method requirements. @@ -148,12 +158,64 @@ namespace hive { namespace protocol { bool operator != ( const price& a, const price& b ); asset operator * ( const asset& a, const price& b ); + /** Applies price to given asset in order to calculate its value in the second asset (like operator* ). + Additionally applies fee scale factor to specific asset in price. Used f.e. to apply fee to + collateralized conversions. Fee scale parameter in basis points. + */ + asset multiply_with_fee( const asset& a, const price& p, uint16_t fee, asset_symbol_type apply_fee_to ); + + /** Represents thin version of asset with fixed symbol. + * MUST NOT be used in operations (it needs to be clear what asset is part of + * operation without access to its definition). Might be used in some custom binary + * API responses where performance is essential and trust is a non-issue. + * This struct is used primarily to hold asset values in chain objects in places + * where use of specific asset is guaranteed. + */ + template< uint32_t _SYMBOL > + struct tiny_asset + { + tiny_asset() {} + tiny_asset( const asset& val ) { set( val ); } + tiny_asset( asset&& val ) { set( val ); } + + asset operator=( const asset& val ) { set( val ); return *this; } + asset operator=( asset&& val ) { set( val ); return *this; } + + share_type amount; + + + asset operator+=( const asset& val ) { check( val ); amount += val.amount; return *this; } + asset operator-=( const asset& val ) { check( val ); amount -= val.amount; return *this; } + + operator asset() const { return to_asset(); } + + asset to_asset() const { return asset( amount, asset_symbol_type::from_asset_num( _SYMBOL ) ); } + + private: + + void set( const asset& val ) { check( val ); amount = val.amount; } + void check( const asset& val ) const { FC_ASSERT( val.symbol.asset_num == _SYMBOL ); } + }; + + template< uint32_t _SYMBOL > + bool operator==( const tiny_asset< _SYMBOL >& obj1, const tiny_asset< _SYMBOL >& obj2 ) { return obj1.to_asset() == obj2.to_asset(); } + + template< uint32_t _SYMBOL > + bool operator!=( const tiny_asset< _SYMBOL >& obj1, const tiny_asset< _SYMBOL >& obj2 ) { return !( obj1.to_asset() == obj2.to_asset() ); } + + template< uint32_t _SYMBOL > + asset operator-( const tiny_asset< _SYMBOL >& obj1) { return -static_cast< asset >( obj1 ); } } } // hive::protocol namespace fc { - void to_variant( const hive::protocol::asset& var, fc::variant& vo ); - void from_variant( const fc::variant& var, hive::protocol::asset& vo ); + void to_variant( const hive::protocol::asset& var, fc::variant& vo ); + void from_variant( const fc::variant& var, hive::protocol::asset& vo ); } FC_REFLECT( hive::protocol::asset, (amount)(symbol) ) FC_REFLECT( hive::protocol::price, (base)(quote) ) + +FC_REFLECT( hive::protocol::HBD_asset, (amount) ) +FC_REFLECT( hive::protocol::HIVE_asset, (amount) ) +FC_REFLECT( hive::protocol::VEST_asset, (amount) ) + diff --git a/libraries/protocol/include/hive/protocol/config.hpp b/libraries/protocol/include/hive/protocol/config.hpp index affb53c8b1..ddf6c9f3f5 100644 --- a/libraries/protocol/include/hive/protocol/config.hpp +++ b/libraries/protocol/include/hive/protocol/config.hpp @@ -3,13 +3,21 @@ */ #pragma once #include +#include // WARNING! // Every symbol defined here needs to be handled appropriately in get_config.cpp // This is checked by get_config_check.sh called from Dockerfile #ifdef IS_TEST_NET -#define HIVE_BLOCKCHAIN_VERSION ( version(1, 25, 0) ) + +using namespace hive::protocol::testnet_blockchain_configuration; + +#ifdef HIVE_ENABLE_SMT + #define HIVE_BLOCKCHAIN_VERSION ( version(1, 26, 0) ) +#else + #define HIVE_BLOCKCHAIN_VERSION ( version(1, 25, 0) ) +#endif #define HIVE_INIT_PRIVATE_KEY (fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("init_key")))) #define HIVE_INIT_PUBLIC_KEY_STR (std::string( hive::protocol::public_key_type(HIVE_INIT_PRIVATE_KEY.get_public_key()) )) @@ -19,7 +27,7 @@ #define HIVE_GENESIS_TIME (fc::time_point_sec(1451606400)) #define HIVE_MINING_TIME (fc::time_point_sec(1451606400)) -#define HIVE_CASHOUT_WINDOW_SECONDS (60*60) /// 1 hr +#define HIVE_CASHOUT_WINDOW_SECONDS configuration_data.get_hive_cashout_window_seconds() #define HIVE_CASHOUT_WINDOW_SECONDS_PRE_HF12 (HIVE_CASHOUT_WINDOW_SECONDS) #define HIVE_CASHOUT_WINDOW_SECONDS_PRE_HF17 (HIVE_CASHOUT_WINDOW_SECONDS) #define HIVE_SECOND_CASHOUT_WINDOW (60*60*24*3) /// 3 days @@ -45,11 +53,24 @@ #define HIVE_PROPOSAL_MAINTENANCE_PERIOD 3600 #define HIVE_PROPOSAL_MAINTENANCE_CLEANUP (60*60*24*1) // 1 day -#define HIVE_DAILY_PROPOSAL_MAINTENANCE_PERIOD (60*60) /// 1 hour +#define HIVE_DAILY_PROPOSAL_MAINTENANCE_PERIOD (60*60) /// 1 hour +#define HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD (fc::days(5)) + +#define HIVE_GLOBAL_REMOVE_THRESHOLD 20 + +#define HIVE_START_MINER_VOTING_BLOCK 30 + +#define HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS (60*60*24*1) /// 1 day +#define HIVE_DELAYED_VOTING_INTERVAL_SECONDS ((HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS)/30) /// We want to have at most 30 entries in the account's delayed voting collection (similary to mainnet) + #else // IS LIVE HIVE NETWORK -#define HIVE_BLOCKCHAIN_VERSION ( version(1, 24, 8) ) +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// LIVE HIVE NETWORK (MainNet) /// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define HIVE_BLOCKCHAIN_VERSION ( version(1, 25, 0) ) #define HIVE_INIT_PUBLIC_KEY_STR "STM8GC13uCZbP44HzMLV6zPZGwVQ8Nt4Kji8PapsPiNq1BK153XTX" #define STEEM_CHAIN_ID fc::sha256() @@ -80,7 +101,16 @@ #define HIVE_PROPOSAL_MAINTENANCE_PERIOD 3600 #define HIVE_PROPOSAL_MAINTENANCE_CLEANUP (60*60*24*1) /// 1 day -#define HIVE_DAILY_PROPOSAL_MAINTENANCE_PERIOD HIVE_ONE_DAY_SECONDS +#define HIVE_DAILY_PROPOSAL_MAINTENANCE_PERIOD HIVE_ONE_DAY_SECONDS +#define HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD (fc::days(365)) + +#define HIVE_GLOBAL_REMOVE_THRESHOLD 200 + +#define HIVE_START_MINER_VOTING_BLOCK (HIVE_BLOCKS_PER_DAY * 30) + +#define HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS (60*60*24*30) // 30 days +#define HIVE_DELAYED_VOTING_INTERVAL_SECONDS (60*60*24*1) // 1 day + #endif @@ -98,7 +128,6 @@ #define HIVE_BLOCKS_PER_YEAR (365*24*60*60/HIVE_BLOCK_INTERVAL) #define HIVE_BLOCKS_PER_DAY (24*60*60/HIVE_BLOCK_INTERVAL) #define HIVE_START_VESTING_BLOCK (HIVE_BLOCKS_PER_DAY * 7) -#define HIVE_START_MINER_VOTING_BLOCK (HIVE_BLOCKS_PER_DAY * 30) #define HIVE_INIT_MINER_NAME "initminer" #define HIVE_NUM_INIT_MINERS 1 @@ -123,6 +152,11 @@ #define HIVE_VESTING_WITHDRAW_INTERVAL_SECONDS (60*60*24*7) /// 1 week per interval #define HIVE_MAX_WITHDRAW_ROUTES 10 #define HIVE_MAX_PENDING_TRANSFERS 255 +#define HIVE_MAX_OPEN_RECURRENT_TRANSFERS 255 +#define HIVE_MAX_CONSECUTIVE_RECURRENT_TRANSFER_FAILURES 10 +#define HIVE_MAX_RECURRENT_TRANSFER_END_DATE 730 /// 2 years in days +#define HIVE_MAX_RECURRENT_TRANSFERS_PER_BLOCK 1000 +#define HIVE_MIN_RECURRENT_TRANSFERS_RECURRENCE 24 #define HIVE_SAVINGS_WITHDRAW_TIME (fc::days(3)) #define HIVE_SAVINGS_WITHDRAW_REQUEST_LIMIT 100 #define HIVE_VOTING_MANA_REGENERATION_SECONDS (5*60*60*24) // 5 day @@ -130,13 +164,12 @@ #define HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF6 (60*30) /// 30 minutes #define HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF20 (60*15) /// 15 minutes #define HIVE_REVERSE_AUCTION_WINDOW_SECONDS_HF21 (60*5) /// 5 minutes +#define HIVE_EARLY_VOTING_SECONDS_HF25 (24*60*60) /// 24 hours +#define HIVE_MID_VOTING_SECONDS_HF25 (48*60*60) /// 48 hours #define HIVE_MIN_VOTE_INTERVAL_SEC 3 #define HIVE_VOTE_DUST_THRESHOLD (50000000) #define HIVE_DOWNVOTE_POOL_PERCENT_HF21 (25*HIVE_1_PERCENT) -#define HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS (60*60*24*30) // 30 days -#define HIVE_DELAYED_VOTING_INTERVAL_SECONDS (60*60*24*1) // 1 day - #define HIVE_MIN_ROOT_COMMENT_INTERVAL (fc::seconds(60*5)) // 5 minutes #define HIVE_MIN_REPLY_INTERVAL (fc::seconds(20)) // 20 seconds #define HIVE_MIN_REPLY_INTERVAL_HF20 (fc::seconds(3)) // 3 seconds @@ -273,6 +306,9 @@ #define HIVE_MIN_FEEDS (HIVE_MAX_WITNESSES/3) /// protects the network from conversions before price has been established #define HIVE_CONVERSION_DELAY_PRE_HF_16 (fc::days(7)) #define HIVE_CONVERSION_DELAY (fc::hours(HIVE_FEED_HISTORY_WINDOW)) //3.5 day conversion +#define HIVE_COLLATERALIZED_CONVERSION_DELAY HIVE_CONVERSION_DELAY +#define HIVE_CONVERSION_COLLATERAL_RATIO (2 * HIVE_100_PERCENT) //has to be at least 100% +#define HIVE_COLLATERALIZED_CONVERSION_FEE (5 * HIVE_1_PERCENT) //has to be positive #define HIVE_MIN_UNDO_HISTORY 10 #define HIVE_MAX_UNDO_HISTORY 10000 diff --git a/libraries/protocol/include/hive/protocol/fixed_string.hpp b/libraries/protocol/include/hive/protocol/fixed_string.hpp index 317438ae83..c2c3d07b1b 100644 --- a/libraries/protocol/include/hive/protocol/fixed_string.hpp +++ b/libraries/protocol/include/hive/protocol/fixed_string.hpp @@ -46,7 +46,6 @@ namespace fc T endian_reverse( const T& x ) { return boost::endian::endian_reverse(x); } - template<> inline uint128 endian_reverse( const uint128& u ) { return uint128( boost::endian::endian_reverse( u.hi ), boost::endian::endian_reverse( u.lo ) ); } diff --git a/libraries/protocol/include/hive/protocol/hive_operations.hpp b/libraries/protocol/include/hive/protocol/hive_operations.hpp index b5fc7affdd..1331fcf9be 100644 --- a/libraries/protocol/include/hive/protocol/hive_operations.hpp +++ b/libraries/protocol/include/hive/protocol/hive_operations.hpp @@ -561,10 +561,12 @@ namespace hive { namespace protocol { struct account_witness_proxy_operation : public base_operation { account_name_type account; - account_name_type proxy; + account_name_type proxy = HIVE_PROXY_TO_SELF_ACCOUNT; void validate()const; void get_required_active_authorities( flat_set& a )const{ a.insert(account); } + + bool is_clearing_proxy() const { return proxy == HIVE_PROXY_TO_SELF_ACCOUNT; } }; @@ -634,19 +636,35 @@ namespace hive { namespace protocol { /** - * This operation instructs the blockchain to start a conversion between HIVE and HBD, + * This operation instructs the blockchain to start a conversion of HBD to HIVE. * The funds are deposited after HIVE_CONVERSION_DELAY */ struct convert_operation : public base_operation { account_name_type owner; uint32_t requestid = 0; - asset amount; + asset amount; //in HBD void validate()const; void get_required_active_authorities( flat_set& a )const{ a.insert(owner); } }; + /** + * Similar to convert_operation, this operation instructs the blockchain to convert HIVE to HBD. + * The operation is performed after HIVE_COLLATERALIZED_CONVERSION_DELAY, but owner gets HBD + * immediately. The price risk is cussioned by extra HIVE (see HIVE_COLLATERAL_RATIO). After actual + * conversion takes place the excess HIVE is returned to the owner. + */ + struct collateralized_convert_operation : public base_operation + { + account_name_type owner; + uint32_t requestid = 0; + asset amount; //in HIVE + + void validate()const; + void get_required_active_authorities( flat_set& a )const { a.insert( owner ); } + }; + /** * This operation creates a limit order and matches it against existing open orders. @@ -1074,7 +1092,36 @@ namespace hive { namespace protocol { void get_required_active_authorities( flat_set< account_name_type >& a ) const { a.insert( delegator ); } void validate() const; }; -} } // hive::protocol + + /** + * @ingroup operations + * + * @brief Creates/updates/removes a recurrent transfer (Transfers any liquid asset (nonvesting) every fixed amount of time from one account to another) + * If amount is set to 0, the recurrent transfer will be deleted + * If there is already a recurrent transfer matching from and to, the recurrent transfer will be updated + */ + struct recurrent_transfer_operation : public base_operation + { + account_name_type from; + /// Account to transfer asset to + account_name_type to; + /// The amount of asset to transfer from @ref from to @ref to + asset amount; + + string memo; + /// How often will the payment be triggered, unit: hours + uint16_t recurrence = 0; + + // How many times the recurrent payment will be executed + uint16_t executions = 0; + /// Extensions. Not currently used. + extensions_type extensions; + + void validate()const; + void get_required_active_authorities( flat_set& a )const{ a.insert(from); } + }; + + } } // hive::protocol FC_REFLECT( hive::protocol::transfer_to_savings_operation, (from)(to)(amount)(memo) ) @@ -1087,6 +1134,7 @@ FC_REFLECT( hive::protocol::set_reset_account_operation, (account)(current_reset FC_REFLECT( hive::protocol::report_over_production_operation, (reporter)(first_block)(second_block) ) FC_REFLECT( hive::protocol::convert_operation, (owner)(requestid)(amount) ) +FC_REFLECT( hive::protocol::collateralized_convert_operation, (owner)(requestid)(amount) ) FC_REFLECT( hive::protocol::feed_publish_operation, (publisher)(exchange_rate) ) FC_REFLECT( hive::protocol::pow, (worker)(input)(signature)(work) ) FC_REFLECT( hive::protocol::pow2, (input)(pow_summary) ) @@ -1182,8 +1230,9 @@ FC_REFLECT( hive::protocol::request_account_recovery_operation, (recovery_accoun FC_REFLECT( hive::protocol::recover_account_operation, (account_to_recover)(new_owner_authority)(recent_owner_authority)(extensions) ); FC_REFLECT( hive::protocol::change_recovery_account_operation, (account_to_recover)(new_recovery_account)(extensions) ); FC_REFLECT( hive::protocol::decline_voting_rights_operation, (account)(decline) ); -FC_REFLECT( hive::protocol::claim_reward_balance_operation, (account)(reward_hive)(reward_hbd)(reward_vests) ) +FC_REFLECT( hive::protocol::claim_reward_balance_operation, (account)(reward_hive)(reward_hbd)(reward_vests) ); #ifdef HIVE_ENABLE_SMT FC_REFLECT( hive::protocol::claim_reward_balance2_operation, (account)(extensions)(reward_tokens) ) #endif FC_REFLECT( hive::protocol::delegate_vesting_shares_operation, (delegator)(delegatee)(vesting_shares) ); +FC_REFLECT( hive::protocol::recurrent_transfer_operation, (from)(to)(amount)(memo)(recurrence)(executions)(extensions) ); diff --git a/libraries/protocol/include/hive/protocol/hive_virtual_operations.hpp b/libraries/protocol/include/hive/protocol/hive_virtual_operations.hpp index 216856632c..7c07234170 100644 --- a/libraries/protocol/include/hive/protocol/hive_virtual_operations.hpp +++ b/libraries/protocol/include/hive/protocol/hive_virtual_operations.hpp @@ -7,10 +7,11 @@ namespace hive { namespace protocol { - struct author_reward_operation : public virtual_operation { - author_reward_operation(){} - author_reward_operation( const account_name_type& a, const string& p, const asset& s, const asset& st, const asset& v, const asset& c ) - :author(a), permlink(p), hbd_payout(s), hive_payout(st), vesting_payout(v), curators_vesting_payout(c) {} + struct author_reward_operation : public virtual_operation + { + author_reward_operation() = default; + author_reward_operation( const account_name_type& a, const string& p, const asset& s, const asset& st, const asset& v, const asset& c, bool must_be_claimed) + :author(a), permlink(p), hbd_payout(s), hive_payout(st), vesting_payout(v), curators_vesting_payout(c), payout_must_be_claimed(must_be_claimed) {} account_name_type author; string permlink; @@ -18,19 +19,25 @@ namespace hive { namespace protocol { asset hive_payout; asset vesting_payout; asset curators_vesting_payout; + /// If set to true, payout has been stored in the separate reward balance, and must be claimed + /// to be transferred to regular balance. + bool payout_must_be_claimed = false; }; struct curation_reward_operation : public virtual_operation { - curation_reward_operation(){} - curation_reward_operation( const string& c, const asset& r, const string& a, const string& p ) - :curator(c), reward(r), comment_author(a), comment_permlink(p) {} + curation_reward_operation() = default; + curation_reward_operation( const string& c, const asset& r, const string& a, const string& p, bool must_be_claimed) + :curator(c), reward(r), comment_author(a), comment_permlink(p), payout_must_be_claimed(must_be_claimed) {} account_name_type curator; asset reward; account_name_type comment_author; string comment_permlink; + /// If set to true, payout has been stored in the separate reward balance, and must be claimed + /// to be transferred to regular balance. + bool payout_must_be_claimed = false; }; @@ -74,14 +81,29 @@ namespace hive { namespace protocol { struct fill_convert_request_operation : public virtual_operation { - fill_convert_request_operation(){} - fill_convert_request_operation( const string& o, const uint32_t id, const asset& in, const asset& out ) + fill_convert_request_operation() = default; + fill_convert_request_operation( const account_name_type& o, const uint32_t id, const HBD_asset& in, const HIVE_asset& out ) :owner(o), requestid(id), amount_in(in), amount_out(out) {} account_name_type owner; uint32_t requestid = 0; - asset amount_in; - asset amount_out; + asset amount_in; //in HBD + asset amount_out; //in HIVE + }; + + struct fill_collateralized_convert_request_operation : public virtual_operation + { + fill_collateralized_convert_request_operation() = default; + fill_collateralized_convert_request_operation( const account_name_type& o, const uint32_t id, + const HIVE_asset& in, const HBD_asset& out, const HIVE_asset& _excess_collateral ) + :owner( o ), requestid( id ), amount_in( in ), amount_out( out ), excess_collateral( _excess_collateral ) + {} + + account_name_type owner; + uint32_t requestid = 0; + asset amount_in; //in HIVE + asset amount_out; //in HBD + asset excess_collateral; //in HIVE }; @@ -98,6 +120,52 @@ namespace hive { namespace protocol { }; + struct transfer_to_vesting_completed_operation : public virtual_operation + { + transfer_to_vesting_completed_operation(){} + transfer_to_vesting_completed_operation( const string& f, const string& t, const asset& s, const asset& v ) + :from_account(f), to_account(t), hive_vested(s), vesting_shares_received(v) {} + + account_name_type from_account; + account_name_type to_account; + asset hive_vested; + asset vesting_shares_received; + }; + + struct pow_reward_operation : public virtual_operation + { + pow_reward_operation(){} + pow_reward_operation( const string& w, const asset& r ) + :worker(w), reward(r) {} + + account_name_type worker; + asset reward; + }; + + struct vesting_shares_split_operation : public virtual_operation + { + vesting_shares_split_operation(){} + vesting_shares_split_operation( const string& o, const asset& old_vests, const asset& new_vests ) + :owner(o), vesting_shares_before_split(old_vests), vesting_shares_after_split(new_vests) {} + + account_name_type owner; + asset vesting_shares_before_split; + asset vesting_shares_after_split; + }; + + struct account_created_operation : public virtual_operation + { + account_created_operation(){} + account_created_operation( const string& new_account_name, const string& creator, const asset& initial_vesting_shares, const asset& initial_delegation ) + :new_account_name(new_account_name), creator(creator), initial_vesting_shares(initial_vesting_shares), initial_delegation(initial_delegation) {} + + account_name_type new_account_name; + account_name_type creator; + asset initial_vesting_shares; + asset initial_delegation; // if created with account_create_with_delegation + }; + + struct shutdown_witness_operation : public virtual_operation { shutdown_witness_operation(){} @@ -278,15 +346,77 @@ namespace hive { namespace protocol { asset hive_transferred; }; + struct expired_account_notification_operation : public virtual_operation + { + expired_account_notification_operation() = default; + expired_account_notification_operation(const account_name_type& acc) + : account(acc) {} + + account_name_type account; + }; + + struct changed_recovery_account_operation : public virtual_operation + { + changed_recovery_account_operation() = default; + changed_recovery_account_operation( const account_name_type& acc, const account_name_type& oldrec, const account_name_type& newrec ) + : account( acc ), old_recovery_account( oldrec ), new_recovery_account( newrec ) {} + + account_name_type account; + account_name_type old_recovery_account; + account_name_type new_recovery_account; + }; + + struct system_warning_operation : public virtual_operation + { + system_warning_operation() = default; + system_warning_operation( const string& _message ) + : message( _message ) {} + + string message; + }; + + + struct fill_recurrent_transfer_operation : public virtual_operation + { + fill_recurrent_transfer_operation() {} + fill_recurrent_transfer_operation(const account_name_type& f,const account_name_type& t, const asset& a, const string& m, uint16_t re) : from( f ), to( t ), amount( a ), memo( m ), remaining_executions(re) {} + + account_name_type from; + account_name_type to; + asset amount; + string memo; + uint16_t remaining_executions = 0; + }; + + struct failed_recurrent_transfer_operation : public virtual_operation + { + failed_recurrent_transfer_operation() {} + failed_recurrent_transfer_operation(const account_name_type& f,const account_name_type& t, const asset& a, uint8_t cf, const string& m, uint16_t re, bool d) : + from( f ), to( t ), amount( a ), memo( m ), consecutive_failures( cf ), remaining_executions(re), deleted( d ) {} + + account_name_type from; + account_name_type to; + asset amount; + string memo; + uint8_t consecutive_failures = 0; + uint16_t remaining_executions = 0; + bool deleted = false; // Indicates that the recurrent transfer was deleted due to too many consecutive failures + }; + } } //hive::protocol -FC_REFLECT( hive::protocol::author_reward_operation, (author)(permlink)(hbd_payout)(hive_payout)(vesting_payout)(curators_vesting_payout) ) -FC_REFLECT( hive::protocol::curation_reward_operation, (curator)(reward)(comment_author)(comment_permlink) ) +FC_REFLECT( hive::protocol::author_reward_operation, (author)(permlink)(hbd_payout)(hive_payout)(vesting_payout)(curators_vesting_payout)(payout_must_be_claimed) ) +FC_REFLECT( hive::protocol::curation_reward_operation, (curator)(reward)(comment_author)(comment_permlink)(payout_must_be_claimed) ) FC_REFLECT( hive::protocol::comment_reward_operation, (author)(permlink)(payout)(author_rewards)(total_payout_value)(curator_payout_value)(beneficiary_payout_value) ) FC_REFLECT( hive::protocol::fill_convert_request_operation, (owner)(requestid)(amount_in)(amount_out) ) +FC_REFLECT( hive::protocol::fill_collateralized_convert_request_operation, (owner)(requestid)(amount_in)(amount_out)(excess_collateral) ) +FC_REFLECT( hive::protocol::account_created_operation, (new_account_name)(creator)(initial_vesting_shares)(initial_delegation) ) FC_REFLECT( hive::protocol::liquidity_reward_operation, (owner)(payout) ) FC_REFLECT( hive::protocol::interest_operation, (owner)(interest) ) FC_REFLECT( hive::protocol::fill_vesting_withdraw_operation, (from_account)(to_account)(withdrawn)(deposited) ) +FC_REFLECT( hive::protocol::transfer_to_vesting_completed_operation, (from_account)(to_account)(hive_vested)(vesting_shares_received) ) +FC_REFLECT( hive::protocol::pow_reward_operation, (worker)(reward) ) +FC_REFLECT( hive::protocol::vesting_shares_split_operation, (owner)(vesting_shares_before_split)(vesting_shares_after_split) ) FC_REFLECT( hive::protocol::shutdown_witness_operation, (owner) ) FC_REFLECT( hive::protocol::fill_order_operation, (current_owner)(current_orderid)(current_pays)(open_owner)(open_orderid)(open_pays) ) FC_REFLECT( hive::protocol::fill_transfer_from_savings_operation, (from)(to)(amount)(request_id)(memo) ) @@ -304,3 +434,8 @@ FC_REFLECT( hive::protocol::sps_fund_operation, (fund_account)(additional_funds) FC_REFLECT( hive::protocol::sps_convert_operation, (fund_account)(hive_amount_in)(hbd_amount_out) ) FC_REFLECT( hive::protocol::hardfork_hive_operation, (account)(treasury)(hbd_transferred)(hive_transferred)(vests_converted)(total_hive_from_vests) ) FC_REFLECT( hive::protocol::hardfork_hive_restore_operation, (account)(treasury)(hbd_transferred)(hive_transferred) ) +FC_REFLECT( hive::protocol::expired_account_notification_operation, (account) ) +FC_REFLECT( hive::protocol::changed_recovery_account_operation, (account)(old_recovery_account)(new_recovery_account) ) +FC_REFLECT( hive::protocol::system_warning_operation, (message) ) +FC_REFLECT( hive::protocol::fill_recurrent_transfer_operation, (from)(to)(amount)(memo)(remaining_executions) ) +FC_REFLECT( hive::protocol::failed_recurrent_transfer_operation, (from)(to)(amount)(memo)(consecutive_failures)(remaining_executions)(deleted) ) diff --git a/libraries/protocol/include/hive/protocol/operation_util.hpp b/libraries/protocol/include/hive/protocol/operation_util.hpp index f53871e64a..20c0e8e65f 100644 --- a/libraries/protocol/include/hive/protocol/operation_util.hpp +++ b/libraries/protocol/include/hive/protocol/operation_util.hpp @@ -37,6 +37,8 @@ struct get_required_auth_visitor template< typename T > void operator()( const T& v )const { + /// Here GCC 10.1 generates fake warning related to uninitialized variables. + /// GCC 10.2 has fixed this problem v.get_required_active_authorities( active ); v.get_required_owner_authorities( owner ); v.get_required_posting_authorities( posting ); diff --git a/libraries/protocol/include/hive/protocol/operations.hpp b/libraries/protocol/include/hive/protocol/operations.hpp index 1f54fa361b..9f9eac1054 100644 --- a/libraries/protocol/include/hive/protocol/operations.hpp +++ b/libraries/protocol/include/hive/protocol/operations.hpp @@ -14,102 +14,114 @@ namespace hive { namespace protocol { * or it will trigger a hardfork. */ typedef fc::static_variant< - vote_operation, - comment_operation, - - transfer_operation, - transfer_to_vesting_operation, - withdraw_vesting_operation, - - limit_order_create_operation, - limit_order_cancel_operation, - - feed_publish_operation, - convert_operation, - - account_create_operation, - account_update_operation, - - witness_update_operation, - account_witness_vote_operation, - account_witness_proxy_operation, - - pow_operation, - - custom_operation, - - report_over_production_operation, - - delete_comment_operation, - custom_json_operation, - comment_options_operation, - set_withdraw_vesting_route_operation, - limit_order_create2_operation, - claim_account_operation, - create_claimed_account_operation, - request_account_recovery_operation, - recover_account_operation, - change_recovery_account_operation, - escrow_transfer_operation, - escrow_dispute_operation, - escrow_release_operation, - pow2_operation, - escrow_approve_operation, - transfer_to_savings_operation, - transfer_from_savings_operation, - cancel_transfer_from_savings_operation, - custom_binary_operation, - decline_voting_rights_operation, - reset_account_operation, - set_reset_account_operation, - claim_reward_balance_operation, - delegate_vesting_shares_operation, - account_create_with_delegation_operation, - witness_set_properties_operation, - account_update2_operation, - create_proposal_operation, - update_proposal_votes_operation, - remove_proposal_operation, - update_proposal_operation, + vote_operation, // 0 + comment_operation, // 1 + + transfer_operation, // 2 + transfer_to_vesting_operation, // 3 + withdraw_vesting_operation, // 4 + + limit_order_create_operation, // 5 + limit_order_cancel_operation, // 6 + + feed_publish_operation, // 7 + convert_operation, // 8 + + account_create_operation, // 9 + account_update_operation, // 10 + + witness_update_operation, // 11 + account_witness_vote_operation, // 12 + account_witness_proxy_operation, // 13 + + pow_operation, // 14 + + custom_operation, // 15 + + report_over_production_operation, // 16 + + delete_comment_operation, // 17 + custom_json_operation, // 18 + comment_options_operation, // 19 + set_withdraw_vesting_route_operation, // 20 + limit_order_create2_operation, // 21 + claim_account_operation, // 22 + create_claimed_account_operation, // 23 + request_account_recovery_operation, // 24 + recover_account_operation, // 25 + change_recovery_account_operation, // 26 + escrow_transfer_operation, // 27 + escrow_dispute_operation, // 28 + escrow_release_operation, // 29 + pow2_operation, // 30 + escrow_approve_operation, // 31 + transfer_to_savings_operation, // 32 + transfer_from_savings_operation, // 33 + cancel_transfer_from_savings_operation, // 34 + custom_binary_operation, // 35 + decline_voting_rights_operation, // 36 + reset_account_operation, // 37 + set_reset_account_operation, // 38 + claim_reward_balance_operation, // 39 + delegate_vesting_shares_operation, // 40 + account_create_with_delegation_operation, // 41 + witness_set_properties_operation, // 42 + account_update2_operation, // 43 + create_proposal_operation, // 44 + update_proposal_votes_operation, // 45 + remove_proposal_operation, // 46 + update_proposal_operation, // 47 + collateralized_convert_operation, // 48 + recurrent_transfer_operation, // 49 #ifdef HIVE_ENABLE_SMT /// SMT operations - claim_reward_balance2_operation, - - smt_setup_operation, - smt_setup_emissions_operation, - smt_set_setup_parameters_operation, - smt_set_runtime_parameters_operation, - smt_create_operation, - smt_contribute_operation, + claim_reward_balance2_operation, // last_pre_smt + 1 + + smt_setup_operation, // last_pre_smt + 2 + smt_setup_emissions_operation, // last_pre_smt + 3 + smt_set_setup_parameters_operation, // last_pre_smt + 4 + smt_set_runtime_parameters_operation, // last_pre_smt + 5 + smt_create_operation, // last_pre_smt + 5 + smt_contribute_operation, // last_pre_smt + 6 #endif /// virtual operations below this point - fill_convert_request_operation, - author_reward_operation, - curation_reward_operation, - comment_reward_operation, - liquidity_reward_operation, - interest_operation, - fill_vesting_withdraw_operation, - fill_order_operation, - shutdown_witness_operation, - fill_transfer_from_savings_operation, - hardfork_operation, - comment_payout_update_operation, - return_vesting_delegation_operation, - comment_benefactor_reward_operation, - producer_reward_operation, - clear_null_account_balance_operation, - proposal_pay_operation, - sps_fund_operation, - hardfork_hive_operation, - hardfork_hive_restore_operation, - delayed_voting_operation, - consolidate_treasury_balance_operation, - effective_comment_vote_operation, - ineffective_delete_comment_operation, - sps_convert_operation + fill_convert_request_operation, // last_regular + 1 + author_reward_operation, // last_regular + 2 + curation_reward_operation, // last_regular + 3 + comment_reward_operation, // last_regular + 4 + liquidity_reward_operation, // last_regular + 5 + interest_operation, // last_regular + 6 + fill_vesting_withdraw_operation, // last_regular + 7 + fill_order_operation, // last_regular + 8 + shutdown_witness_operation, // last_regular + 9 + fill_transfer_from_savings_operation, // last_regular + 10 + hardfork_operation, // last_regular + 11 + comment_payout_update_operation, // last_regular + 12 + return_vesting_delegation_operation, // last_regular + 13 + comment_benefactor_reward_operation, // last_regular + 14 + producer_reward_operation, // last_regular + 15 + clear_null_account_balance_operation, // last_regular + 16 + proposal_pay_operation, // last_regular + 17 + sps_fund_operation, // last_regular + 18 + hardfork_hive_operation, // last_regular + 19 + hardfork_hive_restore_operation, // last_regular + 20 + delayed_voting_operation, // last_regular + 21 + consolidate_treasury_balance_operation, // last_regular + 22 + effective_comment_vote_operation, // last_regular + 23 + ineffective_delete_comment_operation, // last_regular + 24 + sps_convert_operation, // last_regular + 25 + expired_account_notification_operation, // last_regular + 26 + changed_recovery_account_operation, // last_regular + 27 + transfer_to_vesting_completed_operation, // last_regular + 28 + pow_reward_operation, // last_regular + 29 + vesting_shares_split_operation, // last_regular + 30 + account_created_operation, // last_regular + 31 + fill_collateralized_convert_request_operation, // last_regular + 32 + system_warning_operation, // last_regular + 33, + fill_recurrent_transfer_operation, // last_regular + 34 + failed_recurrent_transfer_operation // last_regular + 35 > operation; /*void operation_get_required_authorities( const operation& op, diff --git a/libraries/protocol/include/hive/protocol/sign_state.hpp b/libraries/protocol/include/hive/protocol/sign_state.hpp index 376280d31c..48e3a0fe04 100644 --- a/libraries/protocol/include/hive/protocol/sign_state.hpp +++ b/libraries/protocol/include/hive/protocol/sign_state.hpp @@ -13,7 +13,7 @@ struct sign_state * produce a signature for this key, else returns false. */ bool signed_by( const public_key_type& k ); - bool check_authority( string id ); + bool check_authority( const string& id ); /** * Checks to see if we have signatures of the active authorites of diff --git a/libraries/protocol/include/hive/protocol/sps_operations.hpp b/libraries/protocol/include/hive/protocol/sps_operations.hpp index 6b97a3d20f..9523233580 100644 --- a/libraries/protocol/include/hive/protocol/sps_operations.hpp +++ b/libraries/protocol/include/hive/protocol/sps_operations.hpp @@ -30,21 +30,28 @@ struct create_proposal_operation : public base_operation void get_required_active_authorities( flat_set& a )const { a.insert( creator ); } }; +struct update_proposal_end_date +{ + time_point_sec end_date; +}; + +typedef static_variant< + void_t, + update_proposal_end_date + > update_proposal_extension; + +typedef flat_set< update_proposal_extension > update_proposal_extensions_type; + struct update_proposal_operation : public base_operation { int64_t proposal_id; - account_name_type creator; - /// Amount of HBDs to be daily paid to the `receiver` account, if updated, has to be lower or equal to the current amount asset daily_pay; - string subject; - /// Given link shall be a valid permlink. Must be posted by creator or the receiver. string permlink; - - extensions_type extensions; + update_proposal_extensions_type extensions; void validate()const; @@ -90,9 +97,11 @@ struct remove_proposal_operation : public base_operation struct proposal_pay_operation : public virtual_operation { proposal_pay_operation() = default; - proposal_pay_operation( const account_name_type _receiver, const account_name_type _treasury, const asset& _payment, + proposal_pay_operation( uint32_t _proposal_id, const account_name_type _receiver, const account_name_type _treasury, const asset& _payment, transaction_id_type _trx_id, uint16_t _op_in_trx ) - : receiver( _receiver ), payer( _treasury ), payment( _payment ), trx_id( _trx_id ), op_in_trx( _op_in_trx ) {} + : proposal_id( _proposal_id ), receiver( _receiver ), payer( _treasury ), payment( _payment ), trx_id( _trx_id ), op_in_trx( _op_in_trx ) {} + + uint32_t proposal_id = 0; /// Name of the account which is paid for account_name_type receiver; @@ -155,5 +164,6 @@ FC_REFLECT( hive::protocol::create_proposal_operation, (creator)(receiver)(start FC_REFLECT( hive::protocol::update_proposal_operation, (proposal_id)(creator)(daily_pay)(subject)(permlink)(extensions)) FC_REFLECT( hive::protocol::update_proposal_votes_operation, (voter)(proposal_ids)(approve)(extensions) ) FC_REFLECT( hive::protocol::remove_proposal_operation, (proposal_owner)(proposal_ids)(extensions) ) -FC_REFLECT(hive::protocol::proposal_pay_operation, (receiver)(payer)(payment)(trx_id)(op_in_trx)) +FC_REFLECT(hive::protocol::proposal_pay_operation, (proposal_id)(receiver)(payer)(payment)(trx_id)(op_in_trx)) +FC_REFLECT( hive::protocol::update_proposal_end_date, (end_date) ) diff --git a/libraries/protocol/include/hive/protocol/testnet_blockchain_configuration.hpp b/libraries/protocol/include/hive/protocol/testnet_blockchain_configuration.hpp new file mode 100644 index 0000000000..d019211b16 --- /dev/null +++ b/libraries/protocol/include/hive/protocol/testnet_blockchain_configuration.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace hive { namespace protocol { namespace testnet_blockchain_configuration { + class configuration + { + uint32_t hive_cashout_window_seconds = 60*60;// 1 hr; + + public: + + uint32_t get_hive_cashout_window_seconds() const { return hive_cashout_window_seconds; } + void set_hive_cashout_window_seconds( uint32_t val ){ hive_cashout_window_seconds = val; } + }; + + extern configuration configuration_data; + +} } }// hive::protocol::testnet_blockchain_configuration diff --git a/libraries/protocol/sign_state.cpp b/libraries/protocol/sign_state.cpp index 1366bcd816..5fe07b04fc 100644 --- a/libraries/protocol/sign_state.cpp +++ b/libraries/protocol/sign_state.cpp @@ -16,7 +16,7 @@ bool sign_state::signed_by( const public_key_type& k ) return itr->second = true; } -bool sign_state::check_authority( string id ) +bool sign_state::check_authority( const string& id ) { if( approved_by.find(id) != approved_by.end() ) return true; uint32_t account_auth_count = 1; diff --git a/libraries/protocol/sps_operations.cpp b/libraries/protocol/sps_operations.cpp index fc85026a74..a16b43e9a4 100644 --- a/libraries/protocol/sps_operations.cpp +++ b/libraries/protocol/sps_operations.cpp @@ -25,7 +25,6 @@ void create_proposal_operation::validate()const void update_proposal_operation::validate()const { validate_account_name( creator ); - FC_ASSERT( proposal_id >= 0, "The proposal id can't be negative" ); FC_ASSERT( daily_pay.amount >= 0, "Daily pay can't be negative value" ); FC_ASSERT( daily_pay.symbol.asset_num == HIVE_ASSET_NUM_HBD, "Daily pay should be expressed in HBD"); diff --git a/libraries/protocol/testnet_blockchain_configuration.cpp b/libraries/protocol/testnet_blockchain_configuration.cpp new file mode 100644 index 0000000000..57973f63a3 --- /dev/null +++ b/libraries/protocol/testnet_blockchain_configuration.cpp @@ -0,0 +1,7 @@ +#include + +namespace hive { namespace protocol { namespace testnet_blockchain_configuration { + + configuration configuration_data; + +} } }// hive::protocol::testnet_blockchain_configuration diff --git a/libraries/protocol/version.cpp b/libraries/protocol/version.cpp index 2f160eb658..4db272f538 100644 --- a/libraries/protocol/version.cpp +++ b/libraries/protocol/version.cpp @@ -8,7 +8,7 @@ namespace hive { namespace protocol { /* Quick conversion utilities from http://joelverhagen.com/blog/2010/11/convert-an-int-to-a-string-and-vice-versa-in-c/ */ -inline int string_to_int( fc::string input ) +inline int string_to_int( const fc::string& input ) { std::stringstream s( input ); int i; diff --git a/libraries/utilities/benchmark_dumper.cpp b/libraries/utilities/benchmark_dumper.cpp index b59ed106e3..bbd21be4a1 100644 --- a/libraries/utilities/benchmark_dumper.cpp +++ b/libraries/utilities/benchmark_dumper.cpp @@ -7,7 +7,7 @@ namespace hive { namespace utilities { typedef std::function TScanErrorCallback; -unsigned long long read_u64_value_from(FILE* input, const char* key, unsigned key_length, TScanErrorCallback error_callback) +unsigned long long read_u64_value_from(FILE* input, const char* key, unsigned key_length, const TScanErrorCallback& error_callback) { char line_buffer[PROC_STATUS_LINE_LENGTH]; while( fgets(line_buffer, PROC_STATUS_LINE_LENGTH, input) != nullptr ) @@ -33,7 +33,7 @@ unsigned long long read_u64_value_from(FILE* input, const char* key, unsigned ke bool benchmark_dumper::read_mem(pid_t pid, uint64_t* current_virtual, uint64_t* peak_virtual) { const char* procPath = "/proc/self/status"; - FILE* input = fopen(procPath, "r"); + FILE* input = fopen(procPath, "re"); if(input == NULL) { elog( "cannot read: ${file} file.", ("file", procPath) ); diff --git a/libraries/utilities/include/hive/utilities/logging_config.hpp b/libraries/utilities/include/hive/utilities/logging_config.hpp index 69a61ac37a..e6f2734ab8 100644 --- a/libraries/utilities/include/hive/utilities/logging_config.hpp +++ b/libraries/utilities/include/hive/utilities/logging_config.hpp @@ -17,6 +17,7 @@ struct appender_args std::string appender; std::string file; std::string stream; + std::string time_format; void validate(); }; @@ -36,5 +37,5 @@ fc::optional load_logging_config( const boost::program_optio } } // hive::utilities -FC_REFLECT( hive::utilities::appender_args, (appender)(file)(stream) ) +FC_REFLECT( hive::utilities::appender_args, (appender)(file)(stream)(time_format) ) FC_REFLECT( hive::utilities::logger_args, (name)(level)(appender) ) diff --git a/libraries/utilities/logging_config.cpp b/libraries/utilities/logging_config.cpp index 77ffc40bda..90e9ae0b97 100644 --- a/libraries/utilities/logging_config.cpp +++ b/libraries/utilities/logging_config.cpp @@ -35,6 +35,7 @@ void set_logging_program_options( boost::program_options::options_description& o std::vector< std::string > default_logger( { "{\"name\":\"default\",\"level\":\"info\",\"appender\":\"stderr\"}", + "{\"name\":\"user\",\"level\":\"debug\",\"appender\":\"stderr\"}", "{\"name\":\"p2p\",\"level\":\"warn\",\"appender\":\"p2p\"}" } ); std::string str_default_logger = boost::algorithm::join( default_logger, " " ); @@ -78,7 +79,7 @@ fc::optional load_logging_config( const boost::program_optio for( string& s : all_appenders ) { std::size_t pos = 0; - while ((pos = s.find("{", pos)) != std::string::npos) + while ((pos = s.find('{', pos)) != std::string::npos) { auto appender = fc::json::from_string( s.substr( pos++ ) ).as< appender_args >(); appender.validate(); @@ -96,6 +97,8 @@ fc::optional load_logging_config( const boost::program_optio fc::console_appender::level_color( fc::log_level::error, fc::console_appender::color::red)); console_appender_config.stream = fc::variant( appender.stream ).as< fc::console_appender::stream::type >(); + if (appender.time_format.length()) + console_appender_config.time_format = fc::variant( appender.time_format ).as(); logging_config.appenders.push_back( fc::appender_config( appender.appender, "console", fc::variant( console_appender_config ) ) ); found_logging_config = true; @@ -114,6 +117,8 @@ fc::optional load_logging_config( const boost::program_optio file_appender_config.rotate = true; file_appender_config.rotation_interval = fc::hours(1); file_appender_config.rotation_limit = fc::days(1); + if (appender.time_format.length()) + file_appender_config.time_format = fc::variant( appender.time_format ).as(); logging_config.appenders.push_back( fc::appender_config( appender.appender, "file", fc::variant( file_appender_config ) ) ); found_logging_config = true; @@ -128,7 +133,7 @@ fc::optional load_logging_config( const boost::program_optio for( string& s : loggers ) { std::size_t pos = 0; - while ((pos = s.find("{", pos)) != std::string::npos) + while ((pos = s.find('{', pos)) != std::string::npos) { auto logger = fc::json::from_string( s.substr( pos++ ) ).as< logger_args >(); diff --git a/libraries/vendor/rocksdb/CMakeLists.txt b/libraries/vendor/rocksdb/CMakeLists.txt index 4e89f89b48..81e85f988d 100644 --- a/libraries/vendor/rocksdb/CMakeLists.txt +++ b/libraries/vendor/rocksdb/CMakeLists.txt @@ -32,7 +32,7 @@ # 3. cmake .. # 4. make -j -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.6.3) project(rocksdb) enable_language(CXX) enable_language(C) @@ -186,7 +186,6 @@ else() if(MINGW) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format") endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") include(CheckCXXCompilerFlag) diff --git a/libraries/vendor/rocksdb/db/internal_stats.h b/libraries/vendor/rocksdb/db/internal_stats.h index 6fa8727a4c..dc70ae4a1b 100644 --- a/libraries/vendor/rocksdb/db/internal_stats.h +++ b/libraries/vendor/rocksdb/db/internal_stats.h @@ -216,25 +216,7 @@ class InternalStats { } } - explicit CompactionStats(const CompactionStats& c) - : micros(c.micros), - cpu_micros(c.cpu_micros), - bytes_read_non_output_levels(c.bytes_read_non_output_levels), - bytes_read_output_level(c.bytes_read_output_level), - bytes_written(c.bytes_written), - bytes_moved(c.bytes_moved), - num_input_files_in_non_output_levels( - c.num_input_files_in_non_output_levels), - num_input_files_in_output_level(c.num_input_files_in_output_level), - num_output_files(c.num_output_files), - num_input_records(c.num_input_records), - num_dropped_records(c.num_dropped_records), - count(c.count) { - int num_of_reasons = static_cast(CompactionReason::kNumOfReasons); - for (int i = 0; i < num_of_reasons; i++) { - counts[i] = c.counts[i]; - } - } + explicit CompactionStats(const CompactionStats& c) = default; void Clear() { this->micros = 0; diff --git a/libraries/vendor/rocksdb/db/version_edit.h b/libraries/vendor/rocksdb/db/version_edit.h index 229531792b..ed3c298dea 100644 --- a/libraries/vendor/rocksdb/db/version_edit.h +++ b/libraries/vendor/rocksdb/db/version_edit.h @@ -52,15 +52,6 @@ struct FileDescriptor { smallest_seqno(_smallest_seqno), largest_seqno(_largest_seqno) {} - FileDescriptor& operator=(const FileDescriptor& fd) { - table_reader = fd.table_reader; - packed_number_and_path_id = fd.packed_number_and_path_id; - file_size = fd.file_size; - smallest_seqno = fd.smallest_seqno; - largest_seqno = fd.largest_seqno; - return *this; - } - uint64_t GetNumber() const { return packed_number_and_path_id & kFileNumberMask; } diff --git a/libraries/vendor/rocksdb/utilities/persistent_cache/persistent_cache_util.h b/libraries/vendor/rocksdb/utilities/persistent_cache/persistent_cache_util.h index 214bb5875d..254c038f98 100644 --- a/libraries/vendor/rocksdb/utilities/persistent_cache/persistent_cache_util.h +++ b/libraries/vendor/rocksdb/utilities/persistent_cache/persistent_cache_util.h @@ -48,7 +48,7 @@ class BoundedQueue { T t = std::move(q_.front()); size_ -= t.Size(); q_.pop_front(); - return std::move(t); + return t; } size_t Size() const { diff --git a/libraries/wallet/include/hive/wallet/remote_node_api.hpp b/libraries/wallet/include/hive/wallet/remote_node_api.hpp index eb7c5d92a6..8b2f3b28cb 100644 --- a/libraries/wallet/include/hive/wallet/remote_node_api.hpp +++ b/libraries/wallet/include/hive/wallet/remote_node_api.hpp @@ -53,6 +53,7 @@ struct remote_node_api vector< condenser_api::api_vesting_delegation_expiration_object > get_expiring_vesting_delegations( account_name_type, time_point_sec, uint32_t ); vector< optional< condenser_api::api_witness_object > > get_witnesses( vector< witness_id_type > ); vector< condenser_api::api_convert_request_object > get_conversion_requests( account_name_type ); + vector< condenser_api::api_collateralized_convert_request_object > get_collateralized_conversion_requests( account_name_type ); optional< condenser_api::api_witness_object > get_witness_by_account( account_name_type ); vector< condenser_api::api_witness_object > get_witnesses_by_vote( account_name_type, uint32_t ); vector< account_name_type > lookup_witness_accounts( string, uint32_t ); @@ -66,25 +67,7 @@ struct remote_node_api bool verify_account_authority( string, flat_set< public_key_type > ); vector< tags::vote_state > get_active_votes( account_name_type, string ); vector< condenser_api::account_vote > get_account_votes( account_name_type ); - condenser_api::discussion get_content( account_name_type, string ); - vector< condenser_api::discussion > get_content_replies( account_name_type, string ); vector< tags::tag_count_object > get_tags_used_by_author( account_name_type ); - vector< condenser_api::discussion > get_discussions_by_payout( tags::discussion_query ); - vector< condenser_api::discussion > get_post_discussions_by_payout( tags::discussion_query ); - vector< condenser_api::discussion > get_comment_discussions_by_payout( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_trending( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_created( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_active( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_cashout( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_votes( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_children( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_hot( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_feed( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_blog( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_comments( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_promoted( tags::discussion_query ); - vector< condenser_api::discussion > get_replies_by_last_update( tags::discussion_query ); - vector< condenser_api::discussion > get_discussions_by_author_before_date( tags::discussion_query ); map< uint32_t, condenser_api::api_operation_object > get_account_history( account_name_type, uint64_t, uint32_t ); void broadcast_transaction( condenser_api::legacy_signed_transaction ); condenser_api::broadcast_transaction_synchronous_return broadcast_transaction_synchronous( condenser_api::legacy_signed_transaction ); @@ -106,9 +89,11 @@ struct remote_node_api vector< condenser_api::market_trade > get_recent_trades( uint32_t ); vector< market_history::bucket_object > get_market_history( uint32_t, time_point_sec, time_point_sec ); flat_set< uint32_t > get_market_history_buckets(); + bool is_known_transaction( const transaction_id_type& id )const; vector< condenser_api::api_proposal_object > list_proposals( fc::variant, uint32_t, database_api::sort_order_type, database_api::order_direction_type, database_api::proposal_status ); vector< condenser_api::api_proposal_object > find_proposals( vector< int64_t > ); vector< database_api::api_proposal_vote_object > list_proposal_votes( fc::variant, uint32_t, database_api::sort_order_type, database_api::order_direction_type, database_api::proposal_status ); + vector< database_api::api_recurrent_transfer_object > find_recurrent_transfers( const account_name_type& ); }; } } @@ -146,6 +131,7 @@ FC_API( hive::wallet::remote_node_api, (get_expiring_vesting_delegations) (get_witnesses) (get_conversion_requests) + (get_collateralized_conversion_requests) (get_witness_by_account) (get_witnesses_by_vote) (lookup_witness_accounts) @@ -159,25 +145,7 @@ FC_API( hive::wallet::remote_node_api, (verify_account_authority) (get_active_votes) (get_account_votes) - (get_content) - (get_content_replies) (get_tags_used_by_author) - (get_discussions_by_payout) - (get_post_discussions_by_payout) - (get_comment_discussions_by_payout) - (get_discussions_by_trending) - (get_discussions_by_created) - (get_discussions_by_active) - (get_discussions_by_cashout) - (get_discussions_by_votes) - (get_discussions_by_children) - (get_discussions_by_hot) - (get_discussions_by_feed) - (get_discussions_by_blog) - (get_discussions_by_comments) - (get_discussions_by_promoted) - (get_replies_by_last_update) - (get_discussions_by_author_before_date) (get_account_history) (broadcast_transaction) (broadcast_transaction_synchronous) @@ -199,7 +167,9 @@ FC_API( hive::wallet::remote_node_api, (get_recent_trades) (get_market_history) (get_market_history_buckets) + (is_known_transaction) (list_proposals) (find_proposals) (list_proposal_votes) + (find_recurrent_transfers) ) diff --git a/libraries/wallet/include/hive/wallet/wallet.hpp b/libraries/wallet/include/hive/wallet/wallet.hpp index a735b2c43f..3ec1bd4498 100644 --- a/libraries/wallet/include/hive/wallet/wallet.hpp +++ b/libraries/wallet/include/hive/wallet/wallet.hpp @@ -83,10 +83,10 @@ class wallet_api_impl; class wallet_api { public: - wallet_api( const wallet_data& initial_data, const hive::protocol::chain_id_type& _hive_chain_id, fc::api< remote_node_api > rapi ); + wallet_api( const wallet_data& initial_data, const hive::protocol::chain_id_type& _hive_chain_id, const fc::api< remote_node_api >& rapi ); virtual ~wallet_api(); - bool copy_wallet_file( string destination_filename ); + bool copy_wallet_file( const string& destination_filename ); /** Returns a list of all commands supported by the wallet API. @@ -145,7 +145,7 @@ class wallet_api * @param account Account to query routes * @param type Withdraw type type [incoming, outgoing, all] */ - vector< database_api::api_withdraw_vesting_route_object > get_withdraw_routes( string account, condenser_api::withdraw_route_type type = condenser_api::all )const; + vector< database_api::api_withdraw_vesting_route_object > get_withdraw_routes( const string& account, condenser_api::withdraw_route_type type = condenser_api::all )const; /** * Gets the account information for all accounts for which this wallet has a private key @@ -179,7 +179,7 @@ class wallet_api * @param account_name the name of the account to provide information about * @returns the public account data stored in the blockchain */ - condenser_api::api_account_object get_account( string account_name ) const; + condenser_api::api_account_object get_account( const string& account_name ) const; /** Returns the current wallet filename. * @@ -202,7 +202,7 @@ class wallet_api * @param password - the password to be used at key generation * @return public key corresponding to generated private key, and private key in WIF format. */ - pair get_private_key_from_password( string account, string role, string password )const; + pair get_private_key_from_password( const string& account, const string& role, const string& password )const; /** @@ -238,7 +238,7 @@ class wallet_api * @param password the password previously set with \c set_password() * @ingroup Wallet Management */ - void unlock(string password); + void unlock(const string& password); /** Sets a new password on the wallet. * @@ -246,7 +246,7 @@ class wallet_api * execute this command. * @ingroup Wallet Management */ - void set_password(string password); + void set_password(const string& password); /** Dumps all private keys owned by the wallet. * @@ -316,7 +316,7 @@ class wallet_api * this returns a raw string that may have null characters embedded * in it */ - string serialize_transaction(signed_transaction tx) const; + string serialize_transaction( const signed_transaction& tx) const; /** Imports a WIF Private Key into the wallet to be used to sign transactions by an account. * @@ -324,7 +324,7 @@ class wallet_api * * @param wif_key the WIF Private Key to import */ - bool import_key( string wif_key ); + bool import_key( const string& wif_key ); /** Transforms a brain key to reduce the chance of errors when re-entering the key from memory. * @@ -336,6 +336,28 @@ class wallet_api */ string normalize_brain_key(string s) const; + /** + * This method will claim a subsidized account creation. + * + * @param creator The account to receive the account creation credit + * @param fee The fee to pay for claiming the account (either 0 steem for a discounted account, or the full account fee) + * @param broadcast true if you wish to broadcast the transaction + */ + condenser_api::legacy_signed_transaction claim_account_creation( const string& creator, + const condenser_api::legacy_asset& fee, + bool broadcast )const; + /** + * This method will claim a subsidized account creation without waiting for the transaction to confirm. + * + * @param creator The account to receive the account creation credit + * @param fee The fee to pay for claiming the account (either 0 steem for a discounted account, or the full account fee) + * @param broadcast true if you wish to broadcast the transaction + */ + condenser_api::legacy_signed_transaction claim_account_creation_nonblocking( const string& creator, + const condenser_api::legacy_asset& fee, + bool broadcast )const; + + /** * This method will genrate new owner, active, and memo keys for the new account which * will be controlable by this wallet. There is a fee associated with account creation @@ -347,7 +369,7 @@ class wallet_api * @param json_meta JSON Metadata associated with the new account * @param broadcast true if you wish to broadcast the transaction */ - condenser_api::legacy_signed_transaction create_account( string creator, string new_account_name, string json_meta, bool broadcast ); + condenser_api::legacy_signed_transaction create_account( const string& creator, const string& new_account_name, const string& json_meta, bool broadcast ); /** * This method is used by faucets to create new accounts for other users which must @@ -365,15 +387,46 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction create_account_with_keys( - string creator, - string newname, - string json_meta, + const string& creator, + const string& newname, + const string& json_meta, public_key_type owner, public_key_type active, public_key_type posting, public_key_type memo, bool broadcast )const; + + /** + * This method is used by faucets to create new accounts for other users which must + * provide their desired keys. The resulting account may not be controllable by this + * wallet. There is a fee associated with account creation that is paid by the creator. + * The current account creation fee can be found with the 'info' wallet command. + * This method is identical to create_account_with_keys() except that it also + * adds a 'transfer' operation to the transaction after the create. + * + * @param creator The account creating the new account + * @param newname The name of the new account + * @param initial_amount The amount transferred to the account + * @param memo A memo to send with the transfer + * @param json_meta JSON Metadata associated with the new account + * @param owner public owner key of the new account + * @param active public active key of the new account + * @param posting public posting key of the new account + * @param memo public memo key of the new account + * @param broadcast true if you wish to broadcast the transaction + */ + condenser_api::legacy_signed_transaction create_funded_account_with_keys( const string& creator, + const string& new_account_name, + const condenser_api::legacy_asset& initial_amount, + const string& memo, + const string& json_meta, + public_key_type owner_key, + public_key_type active_key, + public_key_type posting_key, + public_key_type memo_key, + bool broadcast )const; + /** * This method will genrate new owner, active, and memo keys for the new account which * will be controlable by this wallet. There is a fee associated with account creation @@ -390,11 +443,11 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction create_account_delegated( - string creator, - condenser_api::legacy_asset hive_fee, - condenser_api::legacy_asset delegated_vests, - string new_account_name, - string json_meta, + const string& creator, + const condenser_api::legacy_asset& hive_fee, + const condenser_api::legacy_asset& delegated_vests, + const string& new_account_name, + const string& json_meta, bool broadcast ); /** @@ -417,11 +470,11 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction create_account_with_keys_delegated( - string creator, - condenser_api::legacy_asset hive_fee, - condenser_api::legacy_asset delegated_vests, - string newname, - string json_meta, + const string& creator, + const condenser_api::legacy_asset& hive_fee, + const condenser_api::legacy_asset& delegated_vests, + const string& newname, + const string& json_meta, public_key_type owner, public_key_type active, public_key_type posting, @@ -440,8 +493,8 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction update_account( - string accountname, - string json_meta, + const string& accountname, + const string& json_meta, public_key_type owner, public_key_type active, public_key_type posting, @@ -461,7 +514,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction. */ condenser_api::legacy_signed_transaction update_account_auth_key( - string account_name, + const string& account_name, authority_type type, public_key_type key, weight_type weight, @@ -480,9 +533,9 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction. */ condenser_api::legacy_signed_transaction update_account_auth_account( - string account_name, + const string& account_name, authority_type type, - string auth_account, + const string& auth_account, weight_type weight, bool broadcast ); @@ -499,7 +552,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction update_account_auth_threshold( - string account_name, + const string& account_name, authority_type type, uint32_t threshold, bool broadcast ); @@ -512,8 +565,8 @@ class wallet_api * @param broadcast ture if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction update_account_meta( - string account_name, - string json_meta, + const string& account_name, + const string& json_meta, bool broadcast ); /** @@ -524,7 +577,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction update_account_memo_key( - string account_name, + const string& account_name, public_key_type key, bool broadcast ); @@ -537,11 +590,40 @@ class wallet_api * @param vesting_shares The amount of VESTS to delegate * @param broadcast true if you wish to broadcast the transaction */ - condenser_api::legacy_signed_transaction delegate_vesting_shares( - string delegator, - string delegatee, - condenser_api::legacy_asset vesting_shares, - bool broadcast ); + condenser_api::legacy_signed_transaction delegate_vesting_shares( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + bool broadcast ); + + + condenser_api::legacy_signed_transaction delegate_vesting_shares_nonblocking( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + bool broadcast ); + + // these versions also send a regular transfer in the same transaction, intended for sending a .001 STEEM memo + condenser_api::legacy_signed_transaction delegate_vesting_shares_and_transfer( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + const condenser_api::legacy_asset& transfer_amount, + optional transfer_memo, + bool broadcast ); + condenser_api::legacy_signed_transaction delegate_vesting_shares_and_transfer_nonblocking( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + const condenser_api::legacy_asset& transfer_amount, + optional transfer_memo, + bool broadcast ); + + // helper function + condenser_api::legacy_signed_transaction delegate_vesting_shares_and_transfer_and_broadcast( + const string& delegator, const string& delegatee, const condenser_api::legacy_asset& vesting_shares, + optional transfer_amount, optional transfer_memo, + bool broadcast, bool blocking ); /** @@ -568,7 +650,7 @@ class wallet_api * @param owner_account the name or id of the witness account owner, or the id of the witness * @returns the information about the witness stored in the block chain */ - optional< condenser_api::api_witness_object > get_witness(string owner_account); + optional< condenser_api::api_witness_object > get_witness(const string& owner_account); /** Returns conversion requests by an account * @@ -576,8 +658,15 @@ class wallet_api * * @returns All pending conversion requests by account */ - vector< condenser_api::api_convert_request_object > get_conversion_requests( string owner ); + vector< condenser_api::api_convert_request_object > get_conversion_requests( const string& owner ); + /** Returns collateralized conversion requests by an account + * + * @param owner Account name of the account owning the requests + * + * @returns All pending collateralized conversion requests by account + */ + vector< condenser_api::api_collateralized_convert_request_object > get_collateralized_conversion_requests( const string& owner ); /** * Update a witness object owned by the given account. @@ -589,8 +678,8 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction. */ condenser_api::legacy_signed_transaction update_witness( - string witness_name, - string url, + const string& witness_name, + const string& url, public_key_type block_signing_key, const condenser_api::api_chain_properties& props, bool broadcast = false); @@ -602,7 +691,7 @@ class wallet_api * * Setting a vote proxy does not remove your previous votes from the blockchain, * they remain there but are ignored. If you later null out your vote proxy, - * your previous votes will take effect again. + * your previous votes will take effect again (ABW: definitely not true with regard to witness votes). * * This setting can be changed at any time. * @@ -611,8 +700,8 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction set_voting_proxy( - string account_to_modify, - string proxy, + const string& account_to_modify, + const string& proxy, bool broadcast = false); /** @@ -620,6 +709,7 @@ class wallet_api * positively or negatively for a witness. The account can either vote for with positively * votes or against with negative votes. The vote will remain until updated with another * vote. Vote strength is determined by the accounts vesting shares. + * ABW: not true; there are no negative witness votes, you can only remove previously cast vote * * @param account_to_vote_with The account voting for a witness * @param witness_to_vote_for The witness that is being voted for @@ -627,8 +717,8 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction vote_for_witness( - string account_to_vote_with, - string witness_to_vote_for, + const string& account_to_vote_with, + const string& witness_to_vote_for, bool approve = true, bool broadcast = false); @@ -638,14 +728,14 @@ class wallet_api * @param from The account the funds are coming from * @param to The account the funds are going to * @param amount The funds being transferred. i.e. "100.000 HIVE" - * @param memo A memo for the transactionm, encrypted with the to account's public memo key + * @param memo A memo for the transaction, encrypted with the to account's public memo key * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction transfer( - string from, - string to, - condenser_api::legacy_asset amount, - string memo, + const string& from, + const string& to, + const condenser_api::legacy_asset& amount, + const string& memo, bool broadcast = false); /** @@ -664,16 +754,16 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction escrow_transfer( - string from, - string to, - string agent, + const string& from, + const string& to, + const string& agent, uint32_t escrow_id, - condenser_api::legacy_asset hbd_amount, - condenser_api::legacy_asset hive_amount, - condenser_api::legacy_asset fee, - time_point_sec ratification_deadline, - time_point_sec escrow_expiration, - string json_meta, + const condenser_api::legacy_asset& hbd_amount, + const condenser_api::legacy_asset& hive_amount, + const condenser_api::legacy_asset& fee, + const time_point_sec& ratification_deadline, + const time_point_sec& escrow_expiration, + const string& json_meta, bool broadcast = false ); @@ -690,10 +780,10 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction escrow_approve( - string from, - string to, - string agent, - string who, + const string& from, + const string& to, + const string& agent, + const string& who, uint32_t escrow_id, bool approve, bool broadcast = false @@ -710,10 +800,10 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction escrow_dispute( - string from, - string to, - string agent, - string who, + const string& from, + const string& to, + const string& agent, + const string& who, uint32_t escrow_id, bool broadcast = false ); @@ -732,14 +822,14 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction escrow_release( - string from, - string to, - string agent, - string who, - string receiver, + const string& from, + const string& to, + const string& agent, + const string& who, + const string& receiver, uint32_t escrow_id, - condenser_api::legacy_asset hbd_amount, - condenser_api::legacy_asset hive_amount, + const condenser_api::legacy_asset& hbd_amount, + const condenser_api::legacy_asset& hive_amount, bool broadcast = false ); @@ -754,19 +844,54 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction transfer_to_vesting( - string from, - string to, - condenser_api::legacy_asset amount, + const string& from, + const string& to, + const condenser_api::legacy_asset& amount, bool broadcast = false); + + /** + * Transfer funds from one account to another, without waiting for a confirmation. STEEM and SBD can be transferred. + * + * @param from The account the funds are coming from + * @param to The account the funds are going to + * @param amount The funds being transferred. i.e. "100.000 STEEM" + * @param memo A memo for the transactionm, encrypted with the to account's public memo key + * @param broadcast true if you wish to broadcast the transaction + */ + condenser_api::legacy_signed_transaction transfer_nonblocking(const string& from, const string& to, + const condenser_api::legacy_asset& amount, const string& memo, bool broadcast = false); + + // helper function + condenser_api::legacy_signed_transaction transfer_and_broadcast(const string& from, const string& to, + const condenser_api::legacy_asset& amount, const string& memo, bool broadcast, bool blocking ); + /* + * Transfer STEEM into a vesting fund represented by vesting shares (VESTS) without waiting for a confirmation. + * VESTS are required to vesting + * for a minimum of one coin year and can be withdrawn once a week over a two year withdraw period. + * VESTS are protected against dilution up until 90% of STEEM is vesting. + * + * @param from The account the STEEM is coming from + * @param to The account getting the VESTS + * @param amount The amount of STEEM to vest i.e. "100.00 STEEM" + * @param broadcast true if you wish to broadcast the transaction + */ + condenser_api::legacy_signed_transaction transfer_to_vesting_nonblocking(const string& from, const string& to, + const condenser_api::legacy_asset& amount, bool broadcast = false); + + // helper function + condenser_api::legacy_signed_transaction transfer_to_vesting_and_broadcast(const string& from, const string& to, + const condenser_api::legacy_asset& amount, bool broadcast, bool blocking ); + + /** * Transfers into savings happen immediately, transfers from savings take 72 hours */ condenser_api::legacy_signed_transaction transfer_to_savings( - string from, - string to, - condenser_api::legacy_asset amount, - string memo, + const string& from, + const string& to, + const condenser_api::legacy_asset& amount, + const string& memo, bool broadcast = false ); /** @@ -774,15 +899,15 @@ class wallet_api * @param request_id - an unique ID assigned by from account, the id is used to cancel the operation and can be reused after the transfer completes * @param to - the account getting the transfer * @param amount - the amount of assets to be transfered - * @param memo A memo for the transactionm, encrypted with the to account's public memo key + * @param memo A memo for the transaction, encrypted with the to account's public memo key * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction transfer_from_savings( - string from, + const string& from, uint32_t request_id, - string to, - condenser_api::legacy_asset amount, - string memo, + const string& to, + const condenser_api::legacy_asset& amount, + const string& memo, bool broadcast = false ); /** @@ -791,7 +916,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction cancel_transfer_from_savings( - string from, uint32_t + const string& from, uint32_t request_id, bool broadcast = false ); @@ -804,8 +929,8 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction withdraw_vesting( - string from, - condenser_api::legacy_asset vesting_shares, + const string& from, + const condenser_api::legacy_asset& vesting_shares, bool broadcast = false ); /** @@ -821,36 +946,59 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction. */ condenser_api::legacy_signed_transaction set_withdraw_vesting_route( - string from, - string to, + const string& from, + const string& to, uint16_t percent, bool auto_vest, bool broadcast = false ); /** - * This method will convert HBD to HIVE at the current_median_history price one - * week from the time it is executed. This method depends upon there being a valid price feed. + * This method will convert HBD to HIVE at the current_median_history price HIVE_CONVERSION_DELAY + * (3.5 days) from the time it is executed. This method depends upon there being a valid price feed. * * @param from The account requesting conversion of its HBD i.e. "1.000 HBD" * @param amount The amount of HBD to convert * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction convert_hbd( - string from, - condenser_api::legacy_asset amount, + const string& from, + const condenser_api::legacy_asset& amount, + bool broadcast = false ); + + /** + * This method will convert HIVE to HBD at the market_median_history price plus HIVE_COLLATERALIZED_CONVERSION_FEE (5%) + * after HIVE_COLLATERALIZED_CONVERSION_DELAY (3.5 days) from the time it is executed. The caller gets HBD immediately + * calculated at current_min_history corrected with fee (as if conversion took place immediately, but only HIVE amount + * sliced by HIVE_CONVERSION_COLLATERAL_RATIO is calculated - that is, half the amount) and remainder from collateral + * amount is returned after actual conversion takes place. This method depends upon there being a valid price feed. + * + * @param from The account requesting conversion of its HIVE i.e. "2.000 HIVE" + * @param amount The amount of HIVE collateral + * @param broadcast true if you wish to broadcast the transaction + */ + condenser_api::legacy_signed_transaction convert_hive_with_collateral( + const string& from, + const condenser_api::legacy_asset& collateral_amount, bool broadcast = false ); + /** + * Calculates amount of HIVE collateral to be passed to convert_hive_with_collateral in order to immediately get + * given hbd_amount_to_get in HBD. Note that there is no guarantee to get given HBD - when actual transaction + * takes place price might be different than during estimation. + */ + condenser_api::legacy_asset estimate_hive_collateral( const condenser_api::legacy_asset& hbd_amount_to_get ); + /** * A witness can public a price feed for the HIVE:HBD market. The median price feed is used - * to process conversion requests from HBD to HIVE. + * to process conversion requests between HBD and HIVE. * * @param witness The witness publishing the price feed * @param exchange_rate The desired exchange rate * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction publish_feed( - string witness, - condenser_api::legacy_price exchange_rate, + const string& witness, + const condenser_api::legacy_price& exchange_rate, bool broadcast ); /** Signs a transaction. @@ -862,7 +1010,7 @@ class wallet_api * @return the signed version of the transaction */ condenser_api::legacy_signed_transaction sign_transaction( - condenser_api::legacy_signed_transaction tx, + const condenser_api::legacy_signed_transaction& tx, bool broadcast = false); /** Returns an uninitialized object representing a given blockchain operation. @@ -881,7 +1029,7 @@ class wallet_api * (e.g., "global_parameters_update_operation") * @return a default-constructed operation of the given type */ - operation get_prototype_operation(string operation_type); + operation get_prototype_operation( const string& operation_type ); /** * Gets the current order book for HIVE:HBD @@ -889,7 +1037,7 @@ class wallet_api * @param limit Maximum number of orders to return for bids and asks. Max is 1000. */ condenser_api::get_order_book_return get_order_book( uint32_t limit = 1000 ); - vector< condenser_api::api_limit_order_object > get_open_orders( string accountname ); + vector< condenser_api::api_limit_order_object > get_open_orders( const string& accountname ); /** * Creates a limit order at the price amount_to_sell / min_to_receive and will deduct amount_to_sell from account @@ -903,10 +1051,10 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction create_order( - string owner, + const string& owner, uint32_t order_id, - condenser_api::legacy_asset amount_to_sell, - condenser_api::legacy_asset min_to_receive, + const condenser_api::legacy_asset& amount_to_sell, + const condenser_api::legacy_asset& min_to_receive, bool fill_or_kill, uint32_t expiration, bool broadcast ); @@ -919,7 +1067,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction cancel_order( - string owner, + const string& owner, uint32_t orderid, bool broadcast ); @@ -936,13 +1084,13 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction post_comment( - string author, - string permlink, - string parent_author, - string parent_permlink, - string title, - string body, - string json, + const string& author, + const string& permlink, + const string& parent_author, + const string& parent_permlink, + const string& title, + const string& body, + const string& json, bool broadcast ); /** @@ -955,9 +1103,9 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction vote( - string voter, - string author, - string permlink, + const string& voter, + const string& author, + const string& permlink, int16_t weight, bool broadcast ); @@ -978,8 +1126,8 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction request_account_recovery( - string recovery_account, - string account_to_recover, + const string& recovery_account, + const string& account_to_recover, authority new_authority, bool broadcast ); @@ -995,7 +1143,7 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction recover_account( - string account_to_recover, + const string& account_to_recover, authority recent_authority, authority new_authority, bool broadcast ); @@ -1008,11 +1156,11 @@ class wallet_api * @param broadcast true if you wish to broadcast the transaction */ condenser_api::legacy_signed_transaction change_recovery_account( - string owner, - string new_recovery_account, + const string& owner, + const string& new_recovery_account, bool broadcast ); - vector< database_api::api_owner_authority_history_object > get_owner_history( string account )const; + vector< database_api::api_owner_authority_history_object > get_owner_history( const string& account )const; /** * Account operations have sequence numbers from 0 to N where N is the most recent operation. This method @@ -1022,7 +1170,7 @@ class wallet_api * @param from - the absolute sequence number, -1 means most recent, limit is the number of operations before from. * @param limit - the maximum number of items that can be queried (0 to 1000], must be less than from */ - map< uint32_t, condenser_api::api_operation_object > get_account_history( string account, uint32_t from, uint32_t limit ); + map< uint32_t, condenser_api::api_operation_object > get_account_history( const string& account, uint32_t from, uint32_t limit ); FC_TODO(Supplement API argument description) @@ -1034,7 +1182,7 @@ class wallet_api * @param what - a set of things to follow: posts, comments, votes, ignore * @param broadcast true if you wish to broadcast the transaction */ - condenser_api::legacy_signed_transaction follow( string follower, string following, set what, bool broadcast ); + condenser_api::legacy_signed_transaction follow( const string& follower, const string& following, set what, bool broadcast ); std::map> get_result_formatters() const; @@ -1051,20 +1199,23 @@ class wallet_api /** * Returns the encrypted memo if memo starts with '#' otherwise returns memo */ - string get_encrypted_memo( string from, string to, string memo ); + string get_encrypted_memo( const string& from, const string& to, const string& memo ); + + // helper for above + string get_encrypted_memo_using_keys( const public_key_type& from_key, const public_key_type& to_key, string memo ) const; /** * Returns the decrypted memo if possible given wallet's known private keys */ string decrypt_memo( string memo ); - condenser_api::legacy_signed_transaction decline_voting_rights( string account, bool decline, bool broadcast ); + condenser_api::legacy_signed_transaction decline_voting_rights( const string& account, bool decline, bool broadcast ); condenser_api::legacy_signed_transaction claim_reward_balance( - string account, - condenser_api::legacy_asset reward_hive, - condenser_api::legacy_asset reward_hbd, - condenser_api::legacy_asset reward_vests, + const string& account, + const condenser_api::legacy_asset& reward_hive, + const condenser_api::legacy_asset& reward_hbd, + const condenser_api::legacy_asset& reward_vests, bool broadcast ); /** @@ -1075,29 +1226,32 @@ class wallet_api * @param end_date - end date of proposal, * @param daily_pay - the amount of HBD that is being requested to be paid out daily, * @param subject - briefly description of proposal of its title, - * @param url - link to page with description of proposal. + * @param permlink - permlink of the post for the proposal. */ - condenser_api::legacy_signed_transaction create_proposal( account_name_type creator, - account_name_type receiver, + condenser_api::legacy_signed_transaction create_proposal( const account_name_type& creator, + const account_name_type& receiver, time_point_sec start_date, time_point_sec end_date, - condenser_api::legacy_asset daily_pay, + const condenser_api::legacy_asset& daily_pay, string subject, - string url, + string permlink, bool broadcast ); /** * Update worker proposal - * @param creator - the account that creates the proposal, - * @param daily_pay - the amount of HBD that is being requested to be paid out daily, - * @param subject - briefly description of proposal of its title, - * @param permlink - link to page with description of proposal. + * @param proposal_id - the id of the proposal to update, + * @param creator - the account that created the proposal, + * @param daily_pay - new amount of HBD to be requested to be paid out daily, + * @param subject - new description of the proposal, + * @param permlink - new permlink of the post for the proposal. + * @param end_date - new end_date of the proposal. */ condenser_api::legacy_signed_transaction update_proposal( int64_t proposal_id, - account_name_type creator, - condenser_api::legacy_asset daily_pay, + const account_name_type& creator, + const condenser_api::legacy_asset& daily_pay, string subject, - string url, + string permlink, + optional end_date, bool broadcast ); /** * Update existing worker proposal(s) @@ -1105,8 +1259,8 @@ class wallet_api * @param proposals - array with proposal ids, * @param approve - set if proposal(s) should be approved or not. */ - condenser_api::legacy_signed_transaction update_proposal_votes(account_name_type voter, - flat_set< int64_t > proposals, + condenser_api::legacy_signed_transaction update_proposal_votes(const account_name_type& voter, + const flat_set< int64_t >& proposals, bool approve, bool broadcast ); /** @@ -1148,9 +1302,39 @@ class wallet_api * @param deleter - authorized account, * @param ids - proposal ids to be removed. */ - condenser_api::legacy_signed_transaction remove_proposal( account_name_type deleter, - flat_set< int64_t > ids, + condenser_api::legacy_signed_transaction remove_proposal( const account_name_type& deleter, + const flat_set< int64_t >& ids, bool broadcast ); + + /** + * Creates a recurrent transfer of funds from one account to another. HIVE and HBD can be transferred. + * + * @param from The account the funds are coming from + * @param to The account from which the funds are going to + * @param amount The funds being transferred. i.e. "100.000 HIVE" + * @param memo A memo for the transaction, encrypted with the to account's public memo key + * @param recurrence how often the transfer should be executed in hours. eg: 24 + * @param executions how many times should the recurrent transfer be executed + * @param broadcast true if you wish to broadcast the transaction + */ + condenser_api::legacy_signed_transaction recurrent_transfer( + const account_name_type& from, + const account_name_type& to, + const condenser_api::legacy_asset& amount, + const string& memo, + uint16_t recurrence, + uint16_t executions, + bool broadcast ); + + /** + * Finds a recurrent transfer + + * @param from The account from which the funds are coming from + */ + vector< database_api::api_recurrent_transfer_object > find_recurrent_transfers( + const account_name_type& from ); + + }; struct plain_keys { @@ -1198,13 +1382,18 @@ FC_API( hive::wallet::wallet_api, (get_ops_in_block) (get_feed_history) (get_conversion_requests) + (get_collateralized_conversion_requests) (get_account_history) (get_state) (get_withdraw_routes) + (find_recurrent_transfers) /// transaction api + (claim_account_creation) + (claim_account_creation_nonblocking) (create_account) (create_account_with_keys) + (create_funded_account_with_keys) (create_account_delegated) (create_account_with_keys_delegated) (update_account) @@ -1214,19 +1403,26 @@ FC_API( hive::wallet::wallet_api, (update_account_meta) (update_account_memo_key) (delegate_vesting_shares) + (delegate_vesting_shares_nonblocking) + (delegate_vesting_shares_and_transfer) + (delegate_vesting_shares_and_transfer_nonblocking) (update_witness) (set_voting_proxy) (vote_for_witness) (follow) (transfer) + (transfer_nonblocking) (escrow_transfer) (escrow_approve) (escrow_dispute) (escrow_release) (transfer_to_vesting) + (transfer_to_vesting_nonblocking) (withdraw_vesting) (set_withdraw_vesting_route) (convert_hbd) + (convert_hive_with_collateral) + (estimate_hive_collateral) (publish_feed) (get_order_book) (get_open_orders) @@ -1246,6 +1442,7 @@ FC_API( hive::wallet::wallet_api, (decrypt_memo) (decline_voting_rights) (claim_reward_balance) + (recurrent_transfer) /// helper api (get_prototype_operation) diff --git a/libraries/wallet/remote_node_api.cpp b/libraries/wallet/remote_node_api.cpp index 7ee0eba2ce..e6048610b5 100644 --- a/libraries/wallet/remote_node_api.cpp +++ b/libraries/wallet/remote_node_api.cpp @@ -4,391 +4,367 @@ namespace hive { namespace wallet{ // This class exists only to provide method signature information to fc::api, not to execute calls. +// NOLINTNEXTLINE condenser_api::get_version_return remote_node_api::get_version() { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_tag_object > remote_node_api::get_trending_tags( string, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::state remote_node_api::get_state( string ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< account_name_type > remote_node_api::get_active_witnesses() { FC_ASSERT( false ); } +// NOLINTNEXTLINE optional< block_header > remote_node_api::get_block_header( uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE optional< condenser_api::legacy_signed_block > remote_node_api::get_block( uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_operation_object > remote_node_api::get_ops_in_block( uint32_t, bool only_virtual ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE fc::variant_object remote_node_api::get_config() { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::extended_dynamic_global_properties remote_node_api::get_dynamic_global_properties() { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::api_chain_properties remote_node_api::get_chain_properties() { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::legacy_price remote_node_api::get_current_median_history_price() { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::api_feed_history_object remote_node_api::get_feed_history() { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::api_witness_schedule_object remote_node_api::get_witness_schedule() { FC_ASSERT( false ); } +// NOLINTNEXTLINE hardfork_version remote_node_api::get_hardfork_version() { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::scheduled_hardfork remote_node_api::get_next_scheduled_hardfork() { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::api_reward_fund_object remote_node_api::get_reward_fund( string ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< vector< account_name_type > > remote_node_api::get_key_references( vector< public_key_type > ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::extended_account > remote_node_api::get_accounts( vector< account_name_type > ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< account_id_type > remote_node_api::get_account_references( account_id_type account_id ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< optional< condenser_api::api_account_object > > remote_node_api::lookup_account_names( vector< account_name_type > ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< account_name_type > remote_node_api::lookup_accounts( account_name_type, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE uint64_t remote_node_api::get_account_count() { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< database_api::api_owner_authority_history_object > remote_node_api::get_owner_history( account_name_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE optional< database_api::api_account_recovery_request_object > remote_node_api::get_recovery_request( account_name_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE optional< condenser_api::api_escrow_object > remote_node_api::get_escrow( account_name_type, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< database_api::api_withdraw_vesting_route_object > remote_node_api::get_withdraw_routes( account_name_type, condenser_api::withdraw_route_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_savings_withdraw_object > remote_node_api::get_savings_withdraw_from( account_name_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_savings_withdraw_object > remote_node_api::get_savings_withdraw_to( account_name_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_vesting_delegation_object > remote_node_api::get_vesting_delegations( account_name_type, account_name_type, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_vesting_delegation_expiration_object > remote_node_api::get_expiring_vesting_delegations( account_name_type, time_point_sec, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< optional< condenser_api::api_witness_object > > remote_node_api::get_witnesses( vector< witness_id_type > ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_convert_request_object > remote_node_api::get_conversion_requests( account_name_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE +vector< condenser_api::api_collateralized_convert_request_object > remote_node_api::get_collateralized_conversion_requests( account_name_type ) +{ + FC_ASSERT( false ); +} + +// NOLINTNEXTLINE optional< condenser_api::api_witness_object > remote_node_api::get_witness_by_account( account_name_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_witness_object > remote_node_api::get_witnesses_by_vote( account_name_type, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< account_name_type > remote_node_api::lookup_witness_accounts( string, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE uint64_t remote_node_api::get_witness_count() { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_limit_order_object > remote_node_api::get_open_orders( account_name_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE string remote_node_api::get_transaction_hex( condenser_api::legacy_signed_transaction ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::legacy_signed_transaction remote_node_api::get_transaction( transaction_id_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE set< public_key_type > remote_node_api::get_required_signatures( condenser_api::legacy_signed_transaction, flat_set< public_key_type > ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE set< public_key_type > remote_node_api::get_potential_signatures( condenser_api::legacy_signed_transaction ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE bool remote_node_api::verify_authority( condenser_api::legacy_signed_transaction ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE bool remote_node_api::verify_account_authority( string, flat_set< public_key_type > ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< tags::vote_state > remote_node_api::get_active_votes( account_name_type, string ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::account_vote > remote_node_api::get_account_votes( account_name_type ) { FC_ASSERT( false ); } -condenser_api::discussion remote_node_api::get_content( account_name_type, string ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_content_replies( account_name_type, string ) -{ - FC_ASSERT( false ); -} - +// NOLINTNEXTLINE vector< tags::tag_count_object > remote_node_api::get_tags_used_by_author( account_name_type ) { FC_ASSERT( false ); } -vector< condenser_api::discussion > remote_node_api::get_discussions_by_payout( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_post_discussions_by_payout( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_comment_discussions_by_payout( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_trending( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_created( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_active( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_cashout( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_votes( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_children( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_hot( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_feed( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_blog( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_comments( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_promoted( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_replies_by_last_update( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - -vector< condenser_api::discussion > remote_node_api::get_discussions_by_author_before_date( tags::discussion_query ) -{ - FC_ASSERT( false ); -} - +// NOLINTNEXTLINE map< uint32_t, condenser_api::api_operation_object > remote_node_api::get_account_history( account_name_type, uint64_t, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE void remote_node_api::broadcast_transaction( condenser_api::legacy_signed_transaction ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE condenser_api::broadcast_transaction_synchronous_return remote_node_api::broadcast_transaction_synchronous( condenser_api::legacy_signed_transaction ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE void remote_node_api::broadcast_block( signed_block ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< follow::api_follow_object > remote_node_api::get_followers( account_name_type, account_name_type, follow::follow_type, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< follow::api_follow_object > remote_node_api::get_following( account_name_type, account_name_type, follow::follow_type, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE follow::get_follow_count_return remote_node_api::get_follow_count( account_name_type ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< follow::feed_entry > remote_node_api::get_feed_entries( account_name_type, uint32_t, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< follow::comment_feed_entry > remote_node_api::get_feed( account_name_type, uint32_t, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< follow::blog_entry > remote_node_api::get_blog_entries( account_name_type, uint32_t, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< follow::comment_blog_entry > remote_node_api::get_blog( account_name_type, uint32_t, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< follow::account_reputation > remote_node_api::get_account_reputations( account_name_type, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< account_name_type > remote_node_api::get_reblogged_by( account_name_type, string ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< follow::reblog_count > remote_node_api::get_blog_authors( account_name_type ) { FC_ASSERT( false ); @@ -409,16 +385,19 @@ condenser_api::get_order_book_return remote_node_api::get_order_book( uint32_t ) FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::market_trade > remote_node_api::get_trade_history( time_point_sec, time_point_sec, uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::market_trade > remote_node_api::get_recent_trades( uint32_t ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< market_history::bucket_object > remote_node_api::get_market_history( uint32_t, time_point_sec, time_point_sec ) { FC_ASSERT( false ); @@ -429,19 +408,33 @@ flat_set< uint32_t > remote_node_api::get_market_history_buckets() FC_ASSERT( false ); } +bool remote_node_api::is_known_transaction( const transaction_id_type& id )const +{ + FC_ASSERT( false ); +} + +// NOLINTNEXTLINE vector< condenser_api::api_proposal_object > remote_node_api::list_proposals( fc::variant, uint32_t, database_api::sort_order_type, database_api::order_direction_type, database_api::proposal_status ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< condenser_api::api_proposal_object > remote_node_api::find_proposals( vector< int64_t > ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE vector< database_api::api_proposal_vote_object > remote_node_api::list_proposal_votes( fc::variant, uint32_t, database_api::sort_order_type, database_api::order_direction_type, database_api::proposal_status ) { FC_ASSERT( false ); } +// NOLINTNEXTLINE +vector< database_api::api_recurrent_transfer_object > remote_node_api::find_recurrent_transfers( const account_name_type& ) +{ + FC_ASSERT( false ); +} + } } diff --git a/libraries/wallet/wallet.cpp b/libraries/wallet/wallet.cpp index 055a51ff87..ad8be7e808 100644 --- a/libraries/wallet/wallet.cpp +++ b/libraries/wallet/wallet.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -197,13 +198,15 @@ class wallet_api_impl public: api_documentation method_documentation; private: - void enable_umask_protection() { + void enable_umask_protection() + { #ifdef __unix__ _old_umask = umask( S_IRWXG | S_IRWXO ); #endif } - void disable_umask_protection() { + void disable_umask_protection() + { #ifdef __unix__ umask( _old_umask ); #endif @@ -222,7 +225,7 @@ class wallet_api_impl public: wallet_api& self; - wallet_api_impl( wallet_api& s, const wallet_data& initial_data, const hive::protocol::chain_id_type& _hive_chain_id, fc::api< remote_node_api > rapi ) + wallet_api_impl( wallet_api& s, const wallet_data& initial_data, const hive::protocol::chain_id_type& _hive_chain_id, const fc::api< remote_node_api >& rapi ) : self( s ), _remote_api( rapi ) { @@ -246,7 +249,7 @@ class wallet_api_impl } } - bool copy_wallet_file( string destination_filename ) + bool copy_wallet_file( const string& destination_filename ) { fc::path src_path = get_wallet_filename(); if( !fc::exists( src_path ) ) @@ -347,7 +350,7 @@ class wallet_api_impl return result; } - condenser_api::api_account_object get_account( string account_name ) const + condenser_api::api_account_object get_account( const string& account_name ) const { auto accounts = _remote_api->get_accounts( { account_name } ); FC_ASSERT( !accounts.empty(), "Unknown account" ); @@ -375,7 +378,7 @@ class wallet_api_impl fc::ecc::private_key get_private_key_for_account(const condenser_api::api_account_object& account)const { vector active_keys = account.active.get_keys(); - if (active_keys.size() != 1) + if( active_keys.size() != 1 ) FC_THROW("Expecting a simple authority with one active key"); return get_private_key(active_keys.front()); } @@ -384,10 +387,10 @@ class wallet_api_impl // given account name. // @returns true if the key matches a current active/owner/memo key for the named // account, false otherwise (but it is stored either way) - bool import_key(string wif_key) + bool import_key(const string& wif_key) { fc::optional optional_private_key = wif_to_key(wif_key); - if (!optional_private_key) + if( !optional_private_key ) FC_THROW("Invalid private key"); hive::chain::public_key_type wif_pub_key = optional_private_key->get_public_key(); @@ -457,16 +460,16 @@ class wallet_api_impl { int first_unused_index = 0; int number_of_consecutive_unused_keys = 0; - for (int key_index = 0; ; ++key_index) + for( int key_index = 0; ; ++key_index ) { fc::ecc::private_key derived_private_key = derive_private_key(key_to_wif(parent_key), key_index); hive::chain::public_key_type derived_public_key = derived_private_key.get_public_key(); if( _keys.find(derived_public_key) == _keys.end() ) { - if (number_of_consecutive_unused_keys) + if( number_of_consecutive_unused_keys ) { ++number_of_consecutive_unused_keys; - if (number_of_consecutive_unused_keys > 5) + if( number_of_consecutive_unused_keys > 5 ) return first_unused_index; } else @@ -484,9 +487,9 @@ class wallet_api_impl } } - signed_transaction create_account_with_private_key(fc::ecc::private_key owner_privkey, - string account_name, - string creator_account_name, + signed_transaction create_account_with_private_key(const fc::ecc::private_key& owner_privkey, + const string& account_name, + const string& creator_account_name, bool broadcast = false, bool save_wallet = true) { try { @@ -525,7 +528,7 @@ class wallet_api_impl return tx; } FC_CAPTURE_AND_RETHROW( (account_name)(creator_account_name)(broadcast) ) } - signed_transaction set_voting_proxy(string account_to_modify, string proxy, bool broadcast /* = false */) + signed_transaction set_voting_proxy(const string& account_to_modify, const string& proxy, bool broadcast /* = false */) { try { account_witness_proxy_operation op; op.account = account_to_modify; @@ -538,20 +541,61 @@ class wallet_api_impl return sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (account_to_modify)(proxy)(broadcast) ) } - optional< condenser_api::api_witness_object > get_witness( string owner_account ) + optional< condenser_api::api_witness_object > get_witness( const string& owner_account ) { return _remote_api->get_witness_by_account( owner_account ); } + /// Common body for claim_account_creation and claim_account_creation_nonblocking + condenser_api::legacy_signed_transaction build_claim_account_creation(const string& creator, const condenser_api::legacy_asset& fee, + const std::function& tx_signer); + void set_transaction_expiration( uint32_t tx_expiration_seconds ) { FC_ASSERT( tx_expiration_seconds < HIVE_MAX_TIME_UNTIL_EXPIRATION ); _tx_expiration_seconds = tx_expiration_seconds; } - annotated_signed_transaction sign_transaction( + // sets the expiration time and reference block + void initialize_transaction_header(transaction& tx) + { + auto dyn_props = _remote_api->get_dynamic_global_properties(); + tx.set_reference_block( dyn_props.head_block_id ); + tx.set_expiration( dyn_props.time + fc::seconds(_tx_expiration_seconds) ); + } + + // if the user rapidly sends two identical transactions (within the same block), + // the second one will fail because it will have the same transaction id. Waiting + // a few seconds before sending the second transaction will allow it to succeed, + // because the second transaction will be assigned a later expiration time. + // This isn't a good solution for scripts, so we provide this method which + // adds a do-nothing custom operation to the transaction which contains a + // random 64-bit number, which will change the transaction's hash to prevent + // collisions. + void make_transaction_unique(transaction& tx, const std::string& auth) + { + initialize_transaction_header(tx); + if( _remote_api->is_known_transaction( tx.id() ) ) + { + // create a custom operation with a random 64-bit integer which will give this + // transaction a new id + custom_operation custom_op; + custom_op.data.resize(8); + fc::rand_bytes(custom_op.data.data(), custom_op.data.size()); + custom_op.required_auths.insert(auth); + tx.operations.push_back(custom_op); + tx.validate(); + } + } + + condenser_api::legacy_signed_transaction sign_transaction( signed_transaction tx, bool broadcast = false ) + { + return sign_and_broadcast_transaction(std::move(tx), broadcast, true); + } + + condenser_api::legacy_signed_transaction sign_and_broadcast_transaction(signed_transaction tx, bool broadcast, bool blocking) { static const authority null_auth( 1, public_key_type(), 0 ); flat_set< account_name_type > req_active_approvals; @@ -586,16 +630,9 @@ class wallet_api_impl flat_map< string, condenser_api::api_account_object > approving_account_lut; size_t i = 0; - for( const optional< condenser_api::api_account_object >& approving_acct : approving_account_objects ) + for( const auto& approving_acct : approving_account_objects ) { - if( !approving_acct.valid() ) - { - wlog( "operation_get_required_auths said approval of non-existing account ${name} was needed", - ("name", v_approving_account_names[i]) ); - i++; - continue; - } - approving_account_lut[ approving_acct->name ] = *approving_acct; + approving_account_lut[ approving_acct.name ] = approving_acct; i++; } auto get_account_from_lut = [&]( const std::string& name ) -> fc::optional< const condenser_api::api_account_object* > @@ -732,11 +769,19 @@ class wallet_api_impl { try { - auto result = _remote_api->broadcast_transaction_synchronous( condenser_api::legacy_signed_transaction( tx ) ); - annotated_signed_transaction rtrx(tx); - rtrx.block_num = result.block_num; - rtrx.transaction_num = result.trx_num; - return rtrx; + if( blocking ) + { + auto result = _remote_api->broadcast_transaction_synchronous( condenser_api::legacy_signed_transaction( tx ) ); + annotated_signed_transaction rtrx(tx); + rtrx.block_num = result.block_num; + rtrx.transaction_num = result.trx_num; + return rtrx; + } + else + { + _remote_api->broadcast_transaction( condenser_api::legacy_signed_transaction( tx ) ); + return annotated_signed_transaction(tx); + } } catch (const fc::exception& e) { @@ -760,14 +805,16 @@ class wallet_api_impl return result.get_string(); }; - m["list_my_accounts"] = [](variant result, const fc::variants& a ) { + m["list_my_accounts"] = [](variant result, const fc::variants& a ) + { std::stringstream out; auto accounts = result.as>(); asset total_hive; asset total_vest(0, VESTS_SYMBOL ); asset total_hbd(0, HBD_SYMBOL ); - for( const auto& a : accounts ) { + for( const auto& a : accounts ) + { total_hive += a.balance.to_asset(); total_vest += a.vesting_shares.to_asset(); total_hbd += a.hbd_balance.to_asset(); @@ -783,7 +830,8 @@ class wallet_api_impl << std::right << std::setw(16) << legacy_asset::from_asset(total_hbd).to_string() <<"\n"; return out.str(); }; - m["get_account_history"] = []( variant result, const fc::variants& a ) { + m["get_account_history"] = []( variant result, const fc::variants& a ) + { std::stringstream ss; ss << std::left << std::setw( 5 ) << "#" << " "; ss << std::left << std::setw( 10 ) << "BLOCK #" << " "; @@ -792,7 +840,8 @@ class wallet_api_impl ss << std::left << std::setw( 50 ) << "DETAILS" << "\n"; ss << "-------------------------------------------------------------------------------\n"; const auto& results = result.get_array(); - for( const auto& item : results ) { + for( const auto& item : results ) + { ss << std::left << std::setw(5) << item.get_array()[0].as_string() << " "; const auto& op = item.get_array()[1].get_object(); ss << std::left << std::setw(10) << op["block"].as_string() << " "; @@ -803,9 +852,9 @@ class wallet_api_impl } return ss.str(); }; - m["get_open_orders"] = []( variant result, const fc::variants& a ) { + m["get_open_orders"] = []( variant result, const fc::variants& a ) + { auto orders = result.as>(); - std::stringstream ss; ss << setiosflags( ios::fixed ) << setiosflags( ios::left ) ; @@ -814,16 +863,18 @@ class wallet_api_impl ss << ' ' << setw( 10 ) << "Quantity"; ss << ' ' << setw( 10 ) << "Type"; ss << "\n=====================================================================================================\n"; - for( const auto& o : orders ) { + for( const auto& o : orders ) + { ss << ' ' << setw( 10 ) << o.orderid; ss << ' ' << setw( 10 ) << o.real_price; - ss << ' ' << setw( 10 ) << fc::variant( asset( o.for_sale, o.sell_price.base.symbol ) ).as_string(); + ss << ' ' << setw( 10 ) << legacy_asset::from_asset( asset( o.for_sale, o.sell_price.base.symbol ) ).to_string(); ss << ' ' << setw( 10 ) << (o.sell_price.base.symbol == HIVE_SYMBOL ? "SELL" : "BUY"); ss << "\n"; } return ss.str(); }; - m["get_order_book"] = []( variant result, const fc::variants& a ) { + m["get_order_book"] = []( variant result, const fc::variants& a ) + { auto orders = result.as< condenser_api::get_order_book_return >(); std::stringstream ss; asset bid_sum = asset( 0, HBD_SYMBOL ); @@ -846,7 +897,7 @@ class wallet_api_impl for( size_t i = 0; i < orders.bids.size() || i < orders.asks.size(); i++ ) { - if ( i < orders.bids.size() ) + if( i < orders.bids.size() ) { bid_sum += asset( orders.bids[i].hbd, HBD_SYMBOL ); ss @@ -862,7 +913,7 @@ class wallet_api_impl ss << " |"; - if ( i < orders.asks.size() ) + if( i < orders.asks.size() ) { ask_sum += asset( orders.asks[i].hbd, HBD_SYMBOL ); ss << ' ' << setw( spacing ) << orders.asks[i].real_price @@ -905,7 +956,7 @@ class wallet_api_impl return m; } - operation get_prototype_operation( string operation_name ) + operation get_prototype_operation( const string& operation_name ) { auto it = _prototype_ops.find( operation_name ); if( it == _prototype_ops.end() ) @@ -932,19 +983,39 @@ class wallet_api_impl const string _wallet_filename_extension = ".wallet"; }; +condenser_api::legacy_signed_transaction wallet_api_impl::build_claim_account_creation(const string& creator, const condenser_api::legacy_asset& fee, + const std::function& tx_signer) +{ + try + { + FC_ASSERT(!is_locked()); + + auto creator_account = get_account(creator); + claim_account_operation op; + op.creator = creator; + op.fee = fee; + + signed_transaction tx; + tx.operations.push_back(op); + tx.validate(); + + return tx_signer(tx); + } FC_CAPTURE_AND_RETHROW((creator)) +} + } } } // hive::wallet::detail namespace hive { namespace wallet { -wallet_api::wallet_api(const wallet_data& initial_data, const hive::protocol::chain_id_type& _hive_chain_id, fc::api< remote_node_api > rapi) +wallet_api::wallet_api(const wallet_data& initial_data, const hive::protocol::chain_id_type& _hive_chain_id, const fc::api< remote_node_api >& rapi) : my(new detail::wallet_api_impl(*this, initial_data, _hive_chain_id, rapi)) {} wallet_api::~wallet_api(){} -bool wallet_api::copy_wallet_file(string destination_filename) +bool wallet_api::copy_wallet_file(const string& destination_filename) { return my->copy_wallet_file(destination_filename); } @@ -1023,7 +1094,7 @@ brain_key_info wallet_api::suggest_brain_key()const return result; } -string wallet_api::serialize_transaction( signed_transaction tx )const +string wallet_api::serialize_transaction( const signed_transaction& tx )const { return fc::to_hex(fc::raw::pack_to_vector(tx)); } @@ -1034,17 +1105,17 @@ string wallet_api::get_wallet_filename() const } -condenser_api::api_account_object wallet_api::get_account( string account_name ) const +condenser_api::api_account_object wallet_api::get_account( const string& account_name ) const { return my->get_account( account_name ); } -bool wallet_api::import_key(string wif_key) +bool wallet_api::import_key(const string& wif_key) { FC_ASSERT(!is_locked()); // backup wallet fc::optional optional_private_key = wif_to_key(wif_key); - if (!optional_private_key) + if( !optional_private_key ) FC_THROW("Invalid private key"); // string shorthash = detail::pubkey_to_shorthash( optional_private_key->get_public_key() ); // copy_wallet_file( "before-import-key-" + shorthash ); @@ -1060,7 +1131,7 @@ bool wallet_api::import_key(string wif_key) string wallet_api::normalize_brain_key(string s) const { - return detail::normalize_brain_key( s ); + return detail::normalize_brain_key( std::move( s ) ); } variant wallet_api::info() @@ -1070,7 +1141,7 @@ variant wallet_api::info() variant_object wallet_api::about() const { - return my->about(); + return my->about(); } /* @@ -1085,25 +1156,26 @@ vector< account_name_type > wallet_api::list_witnesses(const string& lowerbound, return my->_remote_api->lookup_witness_accounts( lowerbound, limit ); } -optional< condenser_api::api_witness_object > wallet_api::get_witness(string owner_account) +optional< condenser_api::api_witness_object > wallet_api::get_witness( const string& owner_account) { return my->get_witness(owner_account); } -condenser_api::legacy_signed_transaction wallet_api::set_voting_proxy(string account_to_modify, string voting_account, bool broadcast /* = false */) +condenser_api::legacy_signed_transaction wallet_api::set_voting_proxy(const string& account_to_modify, const string& voting_account, bool broadcast /* = false */) { return my->set_voting_proxy(account_to_modify, voting_account, broadcast); } -void wallet_api::set_wallet_filename(string wallet_filename) { my->_wallet_filename = wallet_filename; } +void wallet_api::set_wallet_filename(string wallet_filename) { my->_wallet_filename = std::move(wallet_filename); } condenser_api::legacy_signed_transaction wallet_api::sign_transaction( - condenser_api::legacy_signed_transaction tx, bool broadcast /* = false */) + const condenser_api::legacy_signed_transaction& tx, bool broadcast /* = false */) { try { signed_transaction appbase_tx( tx ); - condenser_api::annotated_signed_transaction result = my->sign_transaction( appbase_tx, broadcast); - return condenser_api::legacy_signed_transaction( result ); + condenser_api::legacy_signed_transaction result = my->sign_transaction( appbase_tx, broadcast); + return result; } FC_CAPTURE_AND_RETHROW( (tx) ) } -operation wallet_api::get_prototype_operation(string operation_name) { +operation wallet_api::get_prototype_operation( const string& operation_name ) +{ return my->get_prototype_operation( operation_name ); } @@ -1111,7 +1183,7 @@ string wallet_api::help()const { std::vector method_names = my->method_documentation.get_method_names(); std::stringstream ss; - for (const std::string method_name : method_names) + for( const std::string& method_name : method_names ) { try { @@ -1132,7 +1204,7 @@ string wallet_api::gethelp(const string& method)const ss << "\n"; std::string doxygenHelpString = my->method_documentation.get_detailed_description(method); - if (!doxygenHelpString.empty()) + if( !doxygenHelpString.empty() ) ss << doxygenHelpString; else ss << "No help defined for method " << method << "\n"; @@ -1142,12 +1214,12 @@ string wallet_api::gethelp(const string& method)const bool wallet_api::load_wallet_file( string wallet_filename ) { - return my->load_wallet_file( wallet_filename ); + return my->load_wallet_file( std::move(wallet_filename) ); } void wallet_api::save_wallet_file( string wallet_filename ) { - my->save_wallet_file( wallet_filename ); + my->save_wallet_file( std::move(wallet_filename) ); } std::map > @@ -1181,7 +1253,7 @@ void wallet_api::lock() my->self.lock_changed(true); } FC_CAPTURE_AND_RETHROW() } -void wallet_api::unlock(string password) +void wallet_api::unlock(const string& password) { try { FC_ASSERT(password.size() > 0); auto pw = fc::sha512::hash(password.c_str(), password.size()); @@ -1193,7 +1265,7 @@ void wallet_api::unlock(string password) my->self.lock_changed(false); } FC_CAPTURE_AND_RETHROW() } -void wallet_api::set_password( string password ) +void wallet_api::set_password( const string& password ) { if( !is_new() ) FC_ASSERT( !is_locked(), "The wallet must be unlocked before the password can be set" ); @@ -1212,7 +1284,7 @@ string wallet_api::get_private_key( public_key_type pubkey )const return key_to_wif( my->get_private_key( pubkey ) ); } -pair wallet_api::get_private_key_from_password( string account, string role, string password )const { +pair wallet_api::get_private_key_from_password( const string& account, const string& role, const string& password )const { auto seed = account + role + password; FC_ASSERT( seed.size() ); auto secret = fc::sha256::hash( seed.c_str(), seed.size() ); @@ -1222,50 +1294,137 @@ pair wallet_api::get_private_key_from_password( string a condenser_api::api_feed_history_object wallet_api::get_feed_history()const { return my->_remote_api->get_feed_history(); } +condenser_api::legacy_signed_transaction wallet_api::claim_account_creation(const string& creator, + const condenser_api::legacy_asset& fee, + bool broadcast )const +{ + return my->build_claim_account_creation(creator, fee, + [this, broadcast](signed_transaction tx) -> condenser_api::legacy_signed_transaction + { + return my->sign_transaction(tx, broadcast); + } + ); +} + +condenser_api::legacy_signed_transaction wallet_api::claim_account_creation_nonblocking(const string& creator, + const condenser_api::legacy_asset& fee, + bool broadcast )const +{ + return my->build_claim_account_creation(creator, fee, + [this, broadcast](signed_transaction tx) ->condenser_api::legacy_signed_transaction + { + return my->sign_and_broadcast_transaction(tx, broadcast, false); + } + ); +} + /** * This method is used by faucets to create new accounts for other users which must * provide their desired keys. The resulting account may not be controllable by this * wallet. */ condenser_api::legacy_signed_transaction wallet_api::create_account_with_keys( - string creator, - string new_account_name, - string json_meta, + const string& creator, + const string& new_account_name, + const string& json_meta, public_key_type owner, public_key_type active, public_key_type posting, public_key_type memo, bool broadcast )const +{ + legacy_asset no_funds; + return create_funded_account_with_keys(creator, new_account_name, no_funds, "", json_meta, owner, + active, posting, memo, broadcast); +} + +/** + * This method is used by faucets to create new accounts for other users which must + * provide their desired keys. The resulting account may not be controllable by this + * wallet. + */ +condenser_api::legacy_signed_transaction wallet_api::create_funded_account_with_keys( const string& creator, + const string& new_account_name, + const condenser_api::legacy_asset& initial_amount, + const string& memo, + const string& json_meta, + public_key_type owner_key, + public_key_type active_key, + public_key_type posting_key, + public_key_type memo_key, + bool broadcast )const { try { FC_ASSERT( !is_locked() ); - account_create_operation op; - op.creator = creator; - op.new_account_name = new_account_name; - op.owner = authority( 1, owner, 1 ); - op.active = authority( 1, active, 1 ); - op.posting = authority( 1, posting, 1 ); - op.memo_key = memo; - op.json_metadata = json_meta; - op.fee = my->_remote_api->get_chain_properties().account_creation_fee; + auto creator_account = get_account(creator); signed_transaction tx; - tx.operations.push_back(op); + if( creator_account.pending_claimed_accounts > 0 ) + { + create_claimed_account_operation op; + op.creator = creator; + op.new_account_name = new_account_name; + op.owner = authority( 1, owner_key, 1 ); + op.active = authority( 1, active_key, 1 ); + op.posting = authority( 1, posting_key, 1 ); + op.memo_key = memo_key; + op.json_metadata = json_meta; + + tx.operations.push_back(op); + } + else + { + account_create_operation op; + op.creator = creator; + op.new_account_name = new_account_name; + op.owner = authority( 1, owner_key, 1 ); + op.active = authority( 1, active_key, 1 ); + op.posting = authority( 1, posting_key, 1 ); + op.memo_key = memo_key; + op.json_metadata = json_meta; + op.fee = my->_remote_api->get_chain_properties().account_creation_fee; + + tx.operations.push_back(op); + } + idump((tx.operations[0])); + + if( initial_amount.amount.value > 0 ) + { + transfer_operation transfer_op; + transfer_op.from = creator; + transfer_op.to = new_account_name; + transfer_op.amount = initial_amount; + + if( memo.size() > 0 && memo[0] == '#' ) + { + auto from_account = get_account( creator ); + transfer_op.memo = get_encrypted_memo_using_keys( from_account.memo_key, memo_key, memo ); + } + else + { + transfer_op.memo = memo; + } + + tx.operations.push_back(transfer_op); + idump((tx.operations[1])); + } + tx.validate(); return my->sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (creator)(new_account_name)(json_meta)(owner)(active)(memo)(broadcast) ) } + /** * This method is used by faucets to create new accounts for other users which must * provide their desired keys. The resulting account may not be controllable by this * wallet. */ condenser_api::legacy_signed_transaction wallet_api::create_account_with_keys_delegated( - string creator, - condenser_api::legacy_asset hive_fee, - condenser_api::legacy_asset delegated_vests, - string new_account_name, - string json_meta, + const string& creator, + const condenser_api::legacy_asset& hive_fee, + const condenser_api::legacy_asset& delegated_vests, + const string& new_account_name, + const string& json_meta, public_key_type owner, public_key_type active, public_key_type posting, @@ -1291,13 +1450,13 @@ condenser_api::legacy_signed_transaction wallet_api::create_account_with_keys_de return my->sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (creator)(new_account_name)(json_meta)(owner)(active)(memo)(broadcast) ) } -condenser_api::legacy_signed_transaction wallet_api::request_account_recovery( string recovery_account, string account_to_recover, authority new_authority, bool broadcast ) +condenser_api::legacy_signed_transaction wallet_api::request_account_recovery( const string& recovery_account, const string& account_to_recover, authority new_authority, bool broadcast ) { FC_ASSERT( !is_locked() ); request_account_recovery_operation op; op.recovery_account = recovery_account; op.account_to_recover = account_to_recover; - op.new_owner_authority = new_authority; + op.new_owner_authority = std::move(new_authority); signed_transaction tx; tx.operations.push_back(op); @@ -1306,13 +1465,14 @@ condenser_api::legacy_signed_transaction wallet_api::request_account_recovery( s return my->sign_transaction( tx, broadcast ); } -condenser_api::legacy_signed_transaction wallet_api::recover_account( string account_to_recover, authority recent_authority, authority new_authority, bool broadcast ) { +condenser_api::legacy_signed_transaction wallet_api::recover_account( const string& account_to_recover, authority recent_authority, authority new_authority, bool broadcast ) +{ FC_ASSERT( !is_locked() ); recover_account_operation op; op.account_to_recover = account_to_recover; - op.new_owner_authority = new_authority; - op.recent_owner_authority = recent_authority; + op.new_owner_authority = std::move(new_authority); + op.recent_owner_authority = std::move(recent_authority); signed_transaction tx; tx.operations.push_back(op); @@ -1321,7 +1481,8 @@ condenser_api::legacy_signed_transaction wallet_api::recover_account( string acc return my->sign_transaction( tx, broadcast ); } -condenser_api::legacy_signed_transaction wallet_api::change_recovery_account( string owner, string new_recovery_account, bool broadcast ) { +condenser_api::legacy_signed_transaction wallet_api::change_recovery_account( const string& owner, const string& new_recovery_account, bool broadcast ) +{ FC_ASSERT( !is_locked() ); change_recovery_account_operation op; @@ -1335,14 +1496,14 @@ condenser_api::legacy_signed_transaction wallet_api::change_recovery_account( st return my->sign_transaction( tx, broadcast ); } -vector< database_api::api_owner_authority_history_object > wallet_api::get_owner_history( string account )const +vector< database_api::api_owner_authority_history_object > wallet_api::get_owner_history( const string& account )const { return my->_remote_api->get_owner_history( account ); } condenser_api::legacy_signed_transaction wallet_api::update_account( - string account_name, - string json_meta, + const string& account_name, + const string& json_meta, public_key_type owner, public_key_type active, public_key_type posting, @@ -1371,7 +1532,7 @@ condenser_api::legacy_signed_transaction wallet_api::update_account( } condenser_api::legacy_signed_transaction wallet_api::update_account_auth_key( - string account_name, + const string& account_name, authority_type type, public_key_type key, weight_type weight, @@ -1414,7 +1575,7 @@ condenser_api::legacy_signed_transaction wallet_api::update_account_auth_key( if( new_auth.is_impossible() ) { - if ( type == owner ) + if( type == owner ) { FC_ASSERT( false, "Owner authority change would render account irrecoverable." ); } @@ -1443,9 +1604,9 @@ condenser_api::legacy_signed_transaction wallet_api::update_account_auth_key( } condenser_api::legacy_signed_transaction wallet_api::update_account_auth_account( - string account_name, + const string& account_name, authority_type type, - string auth_account, + const string& auth_account, weight_type weight, bool broadcast ) { @@ -1486,7 +1647,7 @@ condenser_api::legacy_signed_transaction wallet_api::update_account_auth_account if( new_auth.is_impossible() ) { - if ( type == owner ) + if( type == owner ) { FC_ASSERT( false, "Owner authority change would render account irrecoverable." ); } @@ -1515,7 +1676,7 @@ condenser_api::legacy_signed_transaction wallet_api::update_account_auth_account } condenser_api::legacy_signed_transaction wallet_api::update_account_auth_threshold( - string account_name, + const string& account_name, authority_type type, uint32_t threshold, bool broadcast ) @@ -1551,7 +1712,7 @@ condenser_api::legacy_signed_transaction wallet_api::update_account_auth_thresho if( new_auth.is_impossible() ) { - if ( type == owner ) + if( type == owner ) { FC_ASSERT( false, "Owner authority change would render account irrecoverable." ); } @@ -1580,8 +1741,8 @@ condenser_api::legacy_signed_transaction wallet_api::update_account_auth_thresho } condenser_api::legacy_signed_transaction wallet_api::update_account_meta( - string account_name, - string json_meta, + const string& account_name, + const string& json_meta, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -1603,7 +1764,7 @@ condenser_api::legacy_signed_transaction wallet_api::update_account_meta( } condenser_api::legacy_signed_transaction wallet_api::update_account_memo_key( - string account_name, + const string& account_name, public_key_type key, bool broadcast ) { @@ -1625,11 +1786,14 @@ condenser_api::legacy_signed_transaction wallet_api::update_account_memo_key( return my->sign_transaction( tx, broadcast ); } -condenser_api::legacy_signed_transaction wallet_api::delegate_vesting_shares( - string delegator, - string delegatee, - condenser_api::legacy_asset vesting_shares, - bool broadcast ) +condenser_api::legacy_signed_transaction wallet_api::delegate_vesting_shares_and_transfer_and_broadcast( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + optional transfer_amount, + optional transfer_memo, + bool broadcast, + bool blocking ) { FC_ASSERT( !is_locked() ); @@ -1645,19 +1809,76 @@ condenser_api::legacy_signed_transaction wallet_api::delegate_vesting_shares( signed_transaction tx; tx.operations.push_back( op ); + + if( transfer_amount && transfer_amount->amount.value > 0 ) + { + transfer_operation transfer_op; + transfer_op.from = delegator; + transfer_op.to = delegatee; + transfer_op.amount = *transfer_amount; + transfer_op.memo = transfer_memo ? get_encrypted_memo(delegator, delegatee, *transfer_memo) : ""; + + tx.operations.push_back(transfer_op); + } + tx.validate(); - return my->sign_transaction( tx, broadcast ); + return my->sign_and_broadcast_transaction( tx, broadcast, blocking ); +} + +condenser_api::legacy_signed_transaction wallet_api::delegate_vesting_shares( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + bool broadcast ) +{ + return delegate_vesting_shares_and_transfer_and_broadcast(delegator, delegatee, vesting_shares, + optional(), optional(), broadcast, true); } +condenser_api::legacy_signed_transaction wallet_api::delegate_vesting_shares_nonblocking( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + bool broadcast ) +{ + return delegate_vesting_shares_and_transfer_and_broadcast(delegator, delegatee, vesting_shares, + optional(), optional(), broadcast, false); +} + +condenser_api::legacy_signed_transaction wallet_api::delegate_vesting_shares_and_transfer( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + const condenser_api::legacy_asset& transfer_amount, + optional transfer_memo, + bool broadcast ) +{ + return delegate_vesting_shares_and_transfer_and_broadcast(delegator, delegatee, vesting_shares, + transfer_amount, transfer_memo, broadcast, true); +} + +condenser_api::legacy_signed_transaction wallet_api::delegate_vesting_shares_and_transfer_nonblocking( + const string& delegator, + const string& delegatee, + const condenser_api::legacy_asset& vesting_shares, + const condenser_api::legacy_asset& transfer_amount, + optional transfer_memo, + bool broadcast ) +{ + return delegate_vesting_shares_and_transfer_and_broadcast( delegator, delegatee, vesting_shares, + transfer_amount, transfer_memo, broadcast, false); +} + + /** * This method will genrate new owner, active, and memo keys for the new account which * will be controlable by this wallet. */ condenser_api::legacy_signed_transaction wallet_api::create_account( - string creator, - string new_account_name, - string json_meta, + const string& creator, + const string& new_account_name, + const string& json_meta, bool broadcast ) { try { FC_ASSERT( !is_locked() ); @@ -1677,11 +1898,11 @@ condenser_api::legacy_signed_transaction wallet_api::create_account( * will be controlable by this wallet. */ condenser_api::legacy_signed_transaction wallet_api::create_account_delegated( - string creator, - condenser_api::legacy_asset hive_fee, - condenser_api::legacy_asset delegated_vests, - string new_account_name, - string json_meta, + const string& creator, + const condenser_api::legacy_asset& hive_fee, + const condenser_api::legacy_asset& delegated_vests, + const string& new_account_name, + const string& json_meta, bool broadcast ) { try { FC_ASSERT( !is_locked() ); @@ -1698,11 +1919,11 @@ condenser_api::legacy_signed_transaction wallet_api::create_account_delegated( condenser_api::legacy_signed_transaction wallet_api::update_witness( - string witness_account_name, - string url, + const string& witness_account_name, + const string& url, public_key_type block_signing_key, const condenser_api::api_chain_properties& props, - bool broadcast ) + bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -1733,20 +1954,21 @@ condenser_api::legacy_signed_transaction wallet_api::update_witness( } condenser_api::legacy_signed_transaction wallet_api::vote_for_witness( - string voting_account, - string witness_to_vote_for, + const string& voting_account, + const string& witness_to_vote_for, bool approve, bool broadcast ) { try { FC_ASSERT( !is_locked() ); - account_witness_vote_operation op; - op.account = voting_account; - op.witness = witness_to_vote_for; - op.approve = approve; - signed_transaction tx; - tx.operations.push_back( op ); - tx.validate(); + account_witness_vote_operation op; + op.account = voting_account; + op.witness = witness_to_vote_for; + op.approve = approve; + + signed_transaction tx; + tx.operations.push_back( op ); + tx.validate(); return my->sign_transaction( tx, broadcast ); } FC_CAPTURE_AND_RETHROW( (voting_account)(witness_to_vote_for)(approve)(broadcast) ) } @@ -1809,74 +2031,90 @@ void wallet_api::check_memo( } } -string wallet_api::get_encrypted_memo( - string from, - string to, - string memo ) +string wallet_api::get_encrypted_memo_using_keys( const public_key_type& from_key, const public_key_type& to_key, string memo ) const { - if( memo.size() > 0 && memo[0] == '#' ) - { - memo_data m; + FC_ASSERT( memo.size() > 0 && memo[0] == '#' ); + memo_data m; - auto from_account = get_account( from ); - auto to_account = get_account( to ); + m.from = from_key; + m.to = to_key; + m.nonce = fc::time_point::now().time_since_epoch().count(); - m.from = from_account.memo_key; - m.to = to_account.memo_key; - m.nonce = fc::time_point::now().time_since_epoch().count(); + auto from_priv = my->get_private_key( m.from ); + auto shared_secret = from_priv.get_shared_secret( m.to ); - auto from_priv = my->get_private_key( m.from ); - auto shared_secret = from_priv.get_shared_secret( m.to ); + fc::sha512::encoder enc; + fc::raw::pack( enc, m.nonce ); + fc::raw::pack( enc, shared_secret ); + auto encrypt_key = enc.result(); - fc::sha512::encoder enc; - fc::raw::pack( enc, m.nonce ); - fc::raw::pack( enc, shared_secret ); - auto encrypt_key = enc.result(); + m.encrypted = fc::aes_encrypt( encrypt_key, fc::raw::pack_to_vector(memo.substr(1)) ); + m.check = fc::sha256::hash( encrypt_key )._hash[0]; + return m; +} - m.encrypted = fc::aes_encrypt( encrypt_key, fc::raw::pack_to_vector(memo.substr(1)) ); - m.check = fc::sha256::hash( encrypt_key )._hash[0]; - return m; - } - else +string wallet_api::get_encrypted_memo( const string& from, const string& to, const string& memo ) +{ + if( memo.size() > 0 && memo[0] == '#' ) { + auto from_account = get_account( from ); + auto to_account = get_account( to ); + + return get_encrypted_memo_using_keys(from_account.memo_key, to_account.memo_key, memo); + } else { return memo; } } -condenser_api::legacy_signed_transaction wallet_api::transfer( - string from, - string to, - condenser_api::legacy_asset amount, - string memo, - bool broadcast ) +condenser_api::legacy_signed_transaction wallet_api::transfer(const string& from, const string& to, + const condenser_api::legacy_asset& amount, const string& memo, bool broadcast ) +{ + return transfer_and_broadcast(from, to, amount, memo, broadcast, true); +} + +condenser_api::legacy_signed_transaction wallet_api::transfer_nonblocking(const string& from, const string& to, + const condenser_api::legacy_asset& amount, const string& memo, bool broadcast ) +{ + return transfer_and_broadcast(from, to, amount, memo, broadcast, false); +} + +condenser_api::legacy_signed_transaction wallet_api::transfer_and_broadcast( + const string& from, + const string& to, + const condenser_api::legacy_asset& amount, + const string& memo, + bool broadcast, + bool blocking ) { try { FC_ASSERT( !is_locked() ); - check_memo( memo, get_account( from ) ); - transfer_operation op; - op.from = from; - op.to = to; - op.amount = amount.to_asset(); - op.memo = get_encrypted_memo( from, to, memo ); + check_memo( memo, get_account( from ) ); + transfer_operation op; + op.from = from; + op.to = to; + op.amount = amount.to_asset(); - signed_transaction tx; - tx.operations.push_back( op ); - tx.validate(); + op.memo = get_encrypted_memo( from, to, memo ); - return my->sign_transaction( tx, broadcast ); + signed_transaction tx; + tx.operations.push_back( op ); + tx.validate(); + + my->make_transaction_unique(tx, from); + return my->sign_and_broadcast_transaction( tx, broadcast, blocking ); } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(memo)(broadcast) ) } condenser_api::legacy_signed_transaction wallet_api::escrow_transfer( - string from, - string to, - string agent, + const string& from, + const string& to, + const string& agent, uint32_t escrow_id, - condenser_api::legacy_asset hbd_amount, - condenser_api::legacy_asset hive_amount, - condenser_api::legacy_asset fee, - time_point_sec ratification_deadline, - time_point_sec escrow_expiration, - string json_meta, + const condenser_api::legacy_asset& hbd_amount, + const condenser_api::legacy_asset& hive_amount, + const condenser_api::legacy_asset& fee, + const time_point_sec& ratification_deadline, + const time_point_sec& escrow_expiration, + const string& json_meta, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -1900,10 +2138,10 @@ condenser_api::legacy_signed_transaction wallet_api::escrow_transfer( } condenser_api::legacy_signed_transaction wallet_api::escrow_approve( - string from, - string to, - string agent, - string who, + const string& from, + const string& to, + const string& agent, + const string& who, uint32_t escrow_id, bool approve, bool broadcast ) @@ -1924,10 +2162,10 @@ condenser_api::legacy_signed_transaction wallet_api::escrow_approve( } condenser_api::legacy_signed_transaction wallet_api::escrow_dispute( - string from, - string to, - string agent, - string who, + const string& from, + const string& to, + const string& agent, + const string& who, uint32_t escrow_id, bool broadcast ) { @@ -1947,14 +2185,14 @@ condenser_api::legacy_signed_transaction wallet_api::escrow_dispute( } condenser_api::legacy_signed_transaction wallet_api::escrow_release( - string from, - string to, - string agent, - string who, - string receiver, + const string& from, + const string& to, + const string& agent, + const string& who, + const string& receiver, uint32_t escrow_id, - condenser_api::legacy_asset hbd_amount, - condenser_api::legacy_asset hive_amount, + const condenser_api::legacy_asset& hbd_amount, + const condenser_api::legacy_asset& hive_amount, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -1978,10 +2216,10 @@ condenser_api::legacy_signed_transaction wallet_api::escrow_release( * Transfers into savings happen immediately, transfers from savings take 72 hours */ condenser_api::legacy_signed_transaction wallet_api::transfer_to_savings( - string from, - string to, - condenser_api::legacy_asset amount, - string memo, + const string& from, + const string& to, + const condenser_api::legacy_asset& amount, + const string& memo, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -2003,11 +2241,11 @@ condenser_api::legacy_signed_transaction wallet_api::transfer_to_savings( * @param request_id - an unique ID assigned by from account, the id is used to cancel the operation and can be reused after the transfer completes */ condenser_api::legacy_signed_transaction wallet_api::transfer_from_savings( - string from, + const string& from, uint32_t request_id, - string to, - condenser_api::legacy_asset amount, - string memo, + const string& to, + const condenser_api::legacy_asset& amount, + const string& memo, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -2031,7 +2269,7 @@ condenser_api::legacy_signed_transaction wallet_api::transfer_from_savings( * @param from the account that initiated the transfer */ condenser_api::legacy_signed_transaction wallet_api::cancel_transfer_from_savings( - string from, + const string& from, uint32_t request_id, bool broadcast ) { @@ -2047,27 +2285,38 @@ condenser_api::legacy_signed_transaction wallet_api::cancel_transfer_from_saving } condenser_api::legacy_signed_transaction wallet_api::transfer_to_vesting( - string from, - string to, - condenser_api::legacy_asset amount, - bool broadcast ) + const string& from, const string& to, const condenser_api::legacy_asset& amount, bool broadcast ) +{ + return transfer_to_vesting_and_broadcast(from, to, amount, broadcast, true); +} + +condenser_api::legacy_signed_transaction wallet_api::transfer_to_vesting_nonblocking( + const string& from, const string& to, const condenser_api::legacy_asset& amount, bool broadcast ) +{ + return transfer_to_vesting_and_broadcast(from, to, amount, broadcast, false); +} + +condenser_api::legacy_signed_transaction wallet_api::transfer_to_vesting_and_broadcast( + const string& from, const string& to, const condenser_api::legacy_asset& amount, bool broadcast, bool blocking ) { FC_ASSERT( !is_locked() ); - transfer_to_vesting_operation op; - op.from = from; - op.to = (to == from ? "" : to); - op.amount = amount.to_asset(); + transfer_to_vesting_operation op; + op.from = from; + op.to = (to == from ? "" : to); + op.amount = amount.to_asset(); - signed_transaction tx; - tx.operations.push_back( op ); - tx.validate(); + signed_transaction tx; + tx.operations.push_back( op ); + tx.validate(); - return my->sign_transaction( tx, broadcast ); + my->make_transaction_unique(tx, from); + + return my->sign_and_broadcast_transaction( tx, broadcast, blocking ); } condenser_api::legacy_signed_transaction wallet_api::withdraw_vesting( - string from, - condenser_api::legacy_asset vesting_shares, + const string& from, + const condenser_api::legacy_asset& vesting_shares, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -2083,8 +2332,8 @@ condenser_api::legacy_signed_transaction wallet_api::withdraw_vesting( } condenser_api::legacy_signed_transaction wallet_api::set_withdraw_vesting_route( - string from, - string to, + const string& from, + const string& to, uint16_t percent, bool auto_vest, bool broadcast ) @@ -2104,8 +2353,8 @@ condenser_api::legacy_signed_transaction wallet_api::set_withdraw_vesting_route( } condenser_api::legacy_signed_transaction wallet_api::convert_hbd( - string from, - condenser_api::legacy_asset amount, + const string& from, + const condenser_api::legacy_asset& amount, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -2121,9 +2370,43 @@ condenser_api::legacy_signed_transaction wallet_api::convert_hbd( return my->sign_transaction( tx, broadcast ); } +condenser_api::legacy_signed_transaction wallet_api::convert_hive_with_collateral( + const string& from, + const condenser_api::legacy_asset& collateral_amount, + bool broadcast ) +{ + FC_ASSERT( !is_locked() ); + collateralized_convert_operation op; + op.owner = from; + op.requestid = fc::time_point::now().sec_since_epoch(); + op.amount = collateral_amount.to_asset(); + + signed_transaction tx; + tx.operations.push_back( op ); + tx.validate(); + + return my->sign_transaction( tx, broadcast ); +} + +condenser_api::legacy_asset wallet_api::estimate_hive_collateral( + const condenser_api::legacy_asset& hbd_amount_to_get ) +{ + //must reflect calculations from collateralized_convert_evaluator::do_apply + + auto fhistory = get_feed_history(); + FC_ASSERT( !static_cast( fhistory.current_median_history ).is_null(), "Cannot estimate conversion collateral because there is no price feed." ); + + auto needed_hive = multiply_with_fee( hbd_amount_to_get, fhistory.current_min_history, + HIVE_COLLATERALIZED_CONVERSION_FEE, HIVE_SYMBOL ); + uint128_t _amount = ( uint128_t( needed_hive.amount.value ) * HIVE_CONVERSION_COLLATERAL_RATIO ) / HIVE_100_PERCENT; + asset required_collateral = asset( _amount.to_uint64(), HIVE_SYMBOL ); + + return condenser_api::legacy_asset::from_asset( required_collateral ); +} + condenser_api::legacy_signed_transaction wallet_api::publish_feed( - string witness, - condenser_api::legacy_price exchange_rate, + const string& witness, + const condenser_api::legacy_price& exchange_rate, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -2138,11 +2421,16 @@ condenser_api::legacy_signed_transaction wallet_api::publish_feed( return my->sign_transaction( tx, broadcast ); } -vector< condenser_api::api_convert_request_object > wallet_api::get_conversion_requests( string owner_account ) +vector< condenser_api::api_convert_request_object > wallet_api::get_conversion_requests( const string& owner_account ) { return my->_remote_api->get_conversion_requests( owner_account ); } +vector< condenser_api::api_collateralized_convert_request_object > wallet_api::get_collateralized_conversion_requests( const string& owner_account ) +{ + return my->_remote_api->get_collateralized_conversion_requests( owner_account ); +} + string wallet_api::decrypt_memo( string encrypted_memo ) { if( is_locked() ) @@ -2186,7 +2474,7 @@ string wallet_api::decrypt_memo( string encrypted_memo ) } condenser_api::legacy_signed_transaction wallet_api::decline_voting_rights( - string account, + const string& account, bool decline, bool broadcast ) { @@ -2203,10 +2491,10 @@ condenser_api::legacy_signed_transaction wallet_api::decline_voting_rights( } condenser_api::legacy_signed_transaction wallet_api::claim_reward_balance( - string account, - condenser_api::legacy_asset reward_hive, - condenser_api::legacy_asset reward_hbd, - condenser_api::legacy_asset reward_vests, + const string& account, + const condenser_api::legacy_asset& reward_hive, + const condenser_api::legacy_asset& reward_hbd, + const condenser_api::legacy_asset& reward_vests, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -2223,7 +2511,7 @@ condenser_api::legacy_signed_transaction wallet_api::claim_reward_balance( return my->sign_transaction( tx, broadcast ); } -map< uint32_t, condenser_api::api_operation_object > wallet_api::get_account_history( string account, uint32_t from, uint32_t limit ) +map< uint32_t, condenser_api::api_operation_object > wallet_api::get_account_history( const string& account, uint32_t from, uint32_t limit ) { auto result = my->_remote_api->get_account_history( account, from, limit ); if( !is_locked() ) @@ -2252,10 +2540,10 @@ map< uint32_t, condenser_api::api_operation_object > wallet_api::get_account_his condenser_api::state wallet_api::get_state( string url ) { - return my->_remote_api->get_state( url ); + return my->_remote_api->get_state( std::move( url ) ); } -vector< database_api::api_withdraw_vesting_route_object > wallet_api::get_withdraw_routes( string account, condenser_api::withdraw_route_type type )const +vector< database_api::api_withdraw_vesting_route_object > wallet_api::get_withdraw_routes( const string& account, condenser_api::withdraw_route_type type )const { return my->_remote_api->get_withdraw_routes( account, type ); } @@ -2266,16 +2554,16 @@ condenser_api::get_order_book_return wallet_api::get_order_book( uint32_t limit return my->_remote_api->get_order_book( limit ); } -vector< condenser_api::api_limit_order_object > wallet_api::get_open_orders( string owner ) +vector< condenser_api::api_limit_order_object > wallet_api::get_open_orders( const string& accountname ) { - return my->_remote_api->get_open_orders( owner ); + return my->_remote_api->get_open_orders( accountname ); } condenser_api::legacy_signed_transaction wallet_api::create_order( - string owner, + const string& owner, uint32_t order_id, - condenser_api::legacy_asset amount_to_sell, - condenser_api::legacy_asset min_to_receive, + const condenser_api::legacy_asset& amount_to_sell, + const condenser_api::legacy_asset& min_to_receive, bool fill_or_kill, uint32_t expiration_sec, bool broadcast ) @@ -2297,7 +2585,7 @@ condenser_api::legacy_signed_transaction wallet_api::create_order( } condenser_api::legacy_signed_transaction wallet_api::cancel_order( - string owner, + const string& owner, uint32_t orderid, bool broadcast ) { @@ -2314,13 +2602,13 @@ condenser_api::legacy_signed_transaction wallet_api::cancel_order( } condenser_api::legacy_signed_transaction wallet_api::post_comment( - string author, - string permlink, - string parent_author, - string parent_permlink, - string title, - string body, - string json, + const string& author, + const string& permlink, + const string& parent_author, + const string& parent_permlink, + const string& title, + const string& body, + const string& json, bool broadcast ) { FC_ASSERT( !is_locked() ); @@ -2341,9 +2629,9 @@ condenser_api::legacy_signed_transaction wallet_api::post_comment( } condenser_api::legacy_signed_transaction wallet_api::vote( - string voter, - string author, - string permlink, + const string& voter, + const string& author, + const string& permlink, int16_t weight, bool broadcast ) { @@ -2373,24 +2661,16 @@ condenser_api::legacy_signed_transaction wallet_api::get_transaction( transactio return my->_remote_api->get_transaction( id ); } -condenser_api::legacy_signed_transaction wallet_api::follow( string follower, string following, set what, bool broadcast ) +condenser_api::legacy_signed_transaction wallet_api::follow( const string& follower, const string& following, set what, bool broadcast ) { - auto follwer_account = get_account( follower ); - FC_ASSERT( following.size() ); - if( following[0] != '@' || following[0] != '#' ) - { - following = '@' + following; - } - if( following[0] == '@' ) - { - get_account( following.substr(1) ); - } - FC_ASSERT( following.size() > 1 ); + get_account( follower ); + FC_ASSERT( !following.empty() ); + get_account( following ); follow::follow_operation fop; fop.follower = follower; - fop.following = following; - fop.what = what; + fop.following = '@' + following; + fop.what = std::move( what ); follow::follow_plugin_operation op = fop; custom_json_operation jop; @@ -2405,115 +2685,154 @@ condenser_api::legacy_signed_transaction wallet_api::follow( string follower, st return my->sign_transaction( trx, broadcast ); } - condenser_api::legacy_signed_transaction wallet_api::create_proposal( - account_name_type creator, - account_name_type receiver, - time_point_sec start_date, - time_point_sec end_date, - condenser_api::legacy_asset daily_pay, - string subject, - string permlink, - bool broadcast ) - { - FC_ASSERT( !is_locked() ); +condenser_api::legacy_signed_transaction wallet_api::create_proposal( + const account_name_type& creator, + const account_name_type& receiver, + time_point_sec start_date, + time_point_sec end_date, + const condenser_api::legacy_asset& daily_pay, + string subject, + string permlink, + bool broadcast ) +{ + FC_ASSERT( !is_locked() ); - create_proposal_operation cp; - cp.creator = creator; - cp.receiver = receiver; - cp.start_date = start_date; - cp.end_date = end_date; - cp.daily_pay = daily_pay; - cp.subject = subject; - cp.permlink = permlink; - - signed_transaction trx; - trx.operations.push_back( cp ); - trx.validate(); - return my->sign_transaction( trx, broadcast ); - } + create_proposal_operation cp; + cp.creator = creator; + cp.receiver = receiver; + cp.start_date = start_date; + cp.end_date = end_date; + cp.daily_pay = daily_pay; + cp.subject = std::move( subject ); + cp.permlink = std::move( permlink ); - condenser_api::legacy_signed_transaction wallet_api::update_proposal( - int64_t proposal_id, - account_name_type creator, - condenser_api::legacy_asset daily_pay, - string subject, - string permlink, - bool broadcast ) - { - FC_ASSERT( !is_locked() ); + signed_transaction trx; + trx.operations.push_back( cp ); + trx.validate(); + return my->sign_transaction( trx, broadcast ); +} - update_proposal_operation up; +condenser_api::legacy_signed_transaction wallet_api::update_proposal( + int64_t proposal_id, + const account_name_type& creator, + const condenser_api::legacy_asset& daily_pay, + string subject, + string permlink, + optional end_date, + bool broadcast ) +{ + FC_ASSERT( !is_locked() ); - up.proposal_id = proposal_id; - up.creator = creator; - up.daily_pay = daily_pay; - up.subject = subject; - up.permlink = permlink; + update_proposal_operation up; - signed_transaction trx; - trx.operations.push_back( up ); - trx.validate(); - return my->sign_transaction( trx, broadcast ); + up.proposal_id = proposal_id; + up.creator = creator; + up.daily_pay = daily_pay; + up.subject = std::move(subject); + up.permlink = std::move(permlink); + if( end_date ) + { + update_proposal_end_date ped; + ped.end_date = *end_date; + up.extensions.insert(ped); } - condenser_api::legacy_signed_transaction wallet_api::update_proposal_votes( - account_name_type voter, - flat_set< int64_t > proposals, - bool approve, - bool broadcast ) - { - FC_ASSERT( !is_locked() ); + signed_transaction trx; + trx.operations.push_back( up ); + trx.validate(); + return my->sign_transaction( trx, broadcast ); +} - update_proposal_votes_operation upv; +condenser_api::legacy_signed_transaction wallet_api::update_proposal_votes( + const account_name_type& voter, + const flat_set< int64_t >& proposals, + bool approve, + bool broadcast ) +{ + FC_ASSERT( !is_locked() ); - upv.voter = voter; - upv.proposal_ids = proposals; - upv.approve = approve; + update_proposal_votes_operation upv; - signed_transaction trx; - trx.operations.push_back( upv ); - trx.validate(); - return my->sign_transaction( trx, broadcast ); - } + upv.voter = voter; + upv.proposal_ids = proposals; + upv.approve = approve; - condenser_api::list_proposals_return wallet_api::list_proposals( - fc::variant start, - uint32_t limit, - database_api::sort_order_type order_by, - database_api::order_direction_type order_type, - database_api::proposal_status status ) - { - return my->_remote_api->list_proposals( start, limit, order_by, order_type, status ); - } + signed_transaction trx; + trx.operations.push_back( upv ); + trx.validate(); + return my->sign_transaction( trx, broadcast ); +} - condenser_api::find_proposals_return wallet_api::find_proposals( vector< int64_t > proposal_ids ) - { - return my->_remote_api->find_proposals( proposal_ids ); - } +condenser_api::list_proposals_return wallet_api::list_proposals( + fc::variant start, + uint32_t limit, + database_api::sort_order_type order_by, + database_api::order_direction_type order_type, + database_api::proposal_status status ) +{ + return my->_remote_api->list_proposals( std::move(start), limit, order_by, order_type, status ); +} - condenser_api::list_proposal_votes_return wallet_api::list_proposal_votes( - fc::variant start, - uint32_t limit, - database_api::sort_order_type order_by, - database_api::order_direction_type order_type, - database_api::proposal_status status ) - { - return my->_remote_api->list_proposal_votes( start, limit, order_by, order_type, status ); - } +condenser_api::find_proposals_return wallet_api::find_proposals( vector< int64_t > proposal_ids ) +{ + return my->_remote_api->find_proposals( std::move( proposal_ids ) ); +} - condenser_api::legacy_signed_transaction wallet_api::remove_proposal(account_name_type deleter, - flat_set< int64_t > ids, bool broadcast ) - { +condenser_api::list_proposal_votes_return wallet_api::list_proposal_votes( + fc::variant start, + uint32_t limit, + database_api::sort_order_type order_by, + database_api::order_direction_type order_type, + database_api::proposal_status status ) +{ + return my->_remote_api->list_proposal_votes( std::move( start ), limit, order_by, order_type, status ); +} + +condenser_api::legacy_signed_transaction wallet_api::remove_proposal(const account_name_type& deleter, + const flat_set< int64_t >& ids, bool broadcast ) +{ + FC_ASSERT( !is_locked() ); + + remove_proposal_operation rp; + rp.proposal_owner = deleter; + rp.proposal_ids = ids; + + signed_transaction trx; + trx.operations.push_back( rp ); + trx.validate(); + return my->sign_transaction( trx, broadcast ); +} + +condenser_api::legacy_signed_transaction wallet_api::recurrent_transfer( + const account_name_type& from, + const account_name_type& to, + const condenser_api::legacy_asset& amount, + const string& memo, + uint16_t recurrence, + uint16_t executions, + bool broadcast ) { + try { FC_ASSERT( !is_locked() ); + check_memo( memo, get_account( from ) ); + recurrent_transfer_operation op; + op.from = from; + op.to = to; + op.amount = amount.to_asset(); + op.memo = get_encrypted_memo( from, to, memo ); + op.recurrence = recurrence; + op.executions = executions; - remove_proposal_operation rp; - rp.proposal_owner = deleter; - rp.proposal_ids = ids; + signed_transaction tx; + tx.operations.push_back( op ); + tx.validate(); - signed_transaction trx; - trx.operations.push_back( rp ); - trx.validate(); - return my->sign_transaction( trx, broadcast ); - } + return my->sign_transaction( tx, broadcast ); + } FC_CAPTURE_AND_RETHROW( (from)(to)(amount)(memo)(recurrence)(executions)(broadcast) ) +} + +vector< database_api::api_recurrent_transfer_object > wallet_api::find_recurrent_transfers(const account_name_type& from) +{ + return my->_remote_api->find_recurrent_transfers( from ); +} } } // hive::wallet diff --git a/programs/cli_wallet/CMakeLists.txt b/programs/cli_wallet/CMakeLists.txt index 28d7930667..a7b2d9e1c5 100644 --- a/programs/cli_wallet/CMakeLists.txt +++ b/programs/cli_wallet/CMakeLists.txt @@ -11,7 +11,7 @@ endif() if( HIVE_STATIC_BUILD ) target_link_libraries( cli_wallet PRIVATE - "-static-libstdc++ -static-libgcc -lreadline" + "-static-libstdc++ -static-libgcc" graphene_net hive_chain hive_protocol hive_utilities hive_wallet condenser_api_plugin fc ${CMAKE_DL_LIBS} ${PLATFORM_SPECIFIC_LIBS} ) else( HIVE_STATIC_BUILD ) target_link_libraries( cli_wallet PRIVATE diff --git a/programs/cli_wallet/main.cpp b/programs/cli_wallet/main.cpp index 6aa18e6966..8d70875739 100644 --- a/programs/cli_wallet/main.cpp +++ b/programs/cli_wallet/main.cpp @@ -69,16 +69,21 @@ namespace bpo = boost::program_options; int main( int argc, char** argv ) { try { + const char* raw_password_from_environment = std::getenv("HIVE_WALLET_PASSWORD"); + std::string unlock_password_from_environment = raw_password_from_environment ? raw_password_from_environment : ""; boost::program_options::options_description opts; opts.add_options() ("help,h", "Print this help message and exit.") ("server-rpc-endpoint,s", bpo::value()->implicit_value("ws://127.0.0.1:8090"), "Server websocket RPC endpoint") ("cert-authority,a", bpo::value()->default_value("_default"), "Trusted CA bundle file for connecting to wss:// TLS server") + ("retry-server-connection", "Keep trying to connect to the Server websocket RPC endpoint if the first attempt fails") ("rpc-endpoint,r", bpo::value()->implicit_value("127.0.0.1:8091"), "Endpoint for wallet websocket RPC to listen on") ("rpc-tls-endpoint,t", bpo::value()->implicit_value("127.0.0.1:8092"), "Endpoint for wallet websocket TLS RPC to listen on") ("rpc-tls-certificate,c", bpo::value()->implicit_value("server.pem"), "PEM certificate for wallet websocket TLS RPC") ("rpc-http-endpoint,H", bpo::value()->implicit_value("127.0.0.1:8093"), "Endpoint for wallet HTTP RPC to listen on") + ("unlock", bpo::value()->implicit_value(unlock_password_from_environment), "Password to automatically unlock wallet with " + "or use HIVE_WALLET_PASSWORD environment variable if no password is supplied") ("daemon,d", "Run the wallet in daemon mode" ) ("rpc-http-allowip", bpo::value>()->multitoken(), "Allows only specified IPs to connect to the HTTP endpoint" ) ("wallet-file,w", bpo::value()->implicit_value("wallet.json"), "wallet to load") @@ -163,7 +168,28 @@ int main( int argc, char** argv ) fc::http::websocket_client client( options["cert-authority"].as() ); idump((wdata.ws_server)); - auto con = client.connect( wdata.ws_server ); + fc::http::websocket_connection_ptr con; + + for (;;) + { + try + { + con = client.connect( wdata.ws_server ); + } + catch (const fc::exception& e) + { + if (!options.count("retry-server-connection")) + throw; + } + if (con) + break; + else + { + wlog("Error connecting to server RPC endpoint, retrying in 10 seconds"); + fc::usleep(fc::seconds(10)); + } + } + auto apic = std::make_shared(*con); auto remote_api = apic->get_remote_api< hive::wallet::remote_node_api >( 0, "condenser_api" ); @@ -243,8 +269,8 @@ int main( int argc, char** argv ) _http_server->on_request( [&]( const fc::http::request& req, const fc::http::server::response& resp ) { - auto itr = allowed_ip_set.find( fc::ip::endpoint::from_string(req.remote_endpoint).get_address() ); - if( itr == allowed_ip_set.end() ) { + if( allowed_ip_set.find( fc::ip::endpoint::from_string(req.remote_endpoint).get_address() ) == allowed_ip_set.end() && + allowed_ip_set.find( fc::ip::address() ) == allowed_ip_set.end() ) { elog("rejected connection from ${ip} because it isn't in allowed set ${s}", ("ip",req.remote_endpoint)("s",allowed_ip_set) ); resp.set_status( fc::http::reply::NotAuthorized ); return; @@ -256,6 +282,10 @@ int main( int argc, char** argv ) } ); } + if( options.count("unlock" ) ) { + wapi->unlock( options.at("unlock").as() ); + } + if( !options.count( "daemon" ) ) { wallet_cli->register_api( wapi ); diff --git a/programs/hived/main.cpp b/programs/hived/main.cpp index 244cd4fbcc..35308b0bc6 100644 --- a/programs/hived/main.cpp +++ b/programs/hived/main.cpp @@ -32,7 +32,6 @@ namespace bpo = boost::program_options; using hive::protocol::version; using std::string; -using std::vector; string& version_string() { diff --git a/programs/util/test_shared_mem.cpp b/programs/util/test_shared_mem.cpp index 166fc361a7..34c1a0e60c 100644 --- a/programs/util/test_shared_mem.cpp +++ b/programs/util/test_shared_mem.cpp @@ -112,10 +112,8 @@ int main(int argc, char** argv, char** envp) { try { -#ifndef ENABLE_MIRA bip::managed_mapped_file seg( bip::open_or_create,"./book_container.db", 1024*100); bip::named_mutex mutex( bip::open_or_create,"./book_container.db"); -#endif /* book b( book::allocator_type( seg.get_segment_manager() ) ); @@ -124,7 +122,7 @@ int main(int argc, char** argv, char** envp) b.deq.push_back( shared_string( "hello world", basic_string_allocator( seg.get_segment_manager() ) ) ); idump((b)); */ -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR book_container* pbc = seg.find_or_construct("book container")( book_container::ctor_args_list(), book_container::allocator_type(seg.get_segment_manager())); #else @@ -132,13 +130,14 @@ int main(int argc, char** argv, char** envp) book_container::allocator_type() ); #endif + for( const auto& item : *pbc ) { idump((item)); } //b.pages = pbc->size(); //b.auth = hive::chain::authority( 1, "dan", pbc->size() ); -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR pbc->emplace( [&]( book& b ) { b.name = "emplace name"; b.pages = pbc->size(); @@ -150,7 +149,8 @@ int main(int argc, char** argv, char** envp) }, allocator() ); #endif -#ifndef ENABLE_MIRA + +#ifndef ENABLE_STD_ALLOCATOR t_deque< book > * deq = seg.find_or_construct>("book deque")(allocator(seg.get_segment_manager())); #else t_deque< book > * deq = new chainbase::t_deque( allocator() ); diff --git a/tests/README.md b/tests/README.md index 2a4cbab152..a8009a3aaa 100644 --- a/tests/README.md +++ b/tests/README.md @@ -37,8 +37,6 @@ repository root.) cmake \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_HIVE_TESTNET=ON \ - -DLOW_MEMORY_NODE=OFF \ - -DCLEAR_VOTES=ON \ .. make -j$(nproc) chain_test ./tests/chain_test diff --git a/tests/functional/hive_utils/common.py b/tests/functional/hive_utils/common.py index 982d458528..2bfc58f7ac 100644 --- a/tests/functional/hive_utils/common.py +++ b/tests/functional/hive_utils/common.py @@ -3,6 +3,7 @@ import logging from junit_xml import TestCase import time +import sys import traceback DEFAULT_LOG_FORMAT = '%(asctime)-15s - %(name)s - %(levelname)s - %(message)s' @@ -354,3 +355,38 @@ def log_test_case(*args, **kw): junit_test_cases.append(test_case) return log_test_case +# calculating md5 checsums for all files +def get_index_checksums(path : str): + from subprocess import run, PIPE + import os + + # gather files and paths for md5sum + files = run(["find", path, "-type", "f" ], stdout=PIPE).stdout.decode('utf-8').splitlines() + temp_files = [] + for file in files: + if file.find('snapshot-manifest') < 0: + temp_files.append(file) + files = temp_files + + # calculate md5sum + files_with_md5 = dict() + for file in files: + md5, _, output = run(["md5sum", file ], stdout=PIPE).stdout.decode('utf-8').strip().split(' ') + output = os.path.split(os.path.split(output)[0])[1] + files_with_md5[output] = md5 + + return files_with_md5 + +# return list of missmatched indexes +def compare_snapshots(path_to_first_snapshot : str, path_to_second_snapshot : str) -> list: + first_ret = get_index_checksums(path_to_first_snapshot) + second_ret = get_index_checksums(path_to_second_snapshot) + + keys = first_ret.keys() + ret = [] + for key in keys: + first_md5 = first_ret[key] + second_md5 = second_ret[key] + if first_md5 != second_md5: + ret.append(key) + return ret diff --git a/tests/functional/hive_utils/hive_node.py b/tests/functional/hive_utils/hive_node.py index cb02842408..2c2af3a6d6 100644 --- a/tests/functional/hive_utils/hive_node.py +++ b/tests/functional/hive_utils/hive_node.py @@ -31,7 +31,7 @@ class HiveNode(object): hived_data_dir = None hived_args = list() - def __init__(self, binary_path : str, working_dir : str, binary_args : list): + def __init__(self, binary_path : str, working_dir : str, binary_args : list, stdout_stream = subprocess.PIPE, stderr_stream = None): logger.info("New hive node") if not os.path.exists(binary_path): raise ValueError("Path to hived binary is not valid.") @@ -48,10 +48,13 @@ def __init__(self, binary_path : str, working_dir : str, binary_args : list): if binary_args: self.hived_args.extend(binary_args) + self.stdout_stream = stdout_stream + self.stderr_stream = stderr_stream + def __enter__(self): self.hived_lock.acquire() - from subprocess import Popen, PIPE + from subprocess import Popen, PIPE, DEVNULL from time import sleep hived_command = [ @@ -60,12 +63,14 @@ def __enter__(self): ] hived_command.extend(self.hived_args) - self.hived_process = Popen(hived_command, stdout=PIPE, stderr=None) + self.hived_process = Popen(hived_command, stdout=self.stdout_stream, stderr=self.stderr_stream) self.hived_process.poll() sleep(5) if self.hived_process.returncode: raise Exception("Error during starting node") + + self.last_returncode = None def get_output(self): out, err = self.hived_process.communicate() @@ -75,22 +80,36 @@ def __exit__(self, exc, value, tb): logger.info("Closing node") from signal import SIGINT, SIGTERM from time import sleep + from psutil import pid_exists if self.hived_process is not None: self.hived_process.poll() - if self.hived_process.returncode != 0: + if pid_exists(self.hived_process.pid): self.hived_process.send_signal(SIGINT) sleep(7) self.hived_process.poll() - if self.hived_process.returncode != 0: + if pid_exists(self.hived_process.pid): self.hived_process.send_signal(SIGTERM) sleep(7) self.hived_process.poll() - if self.hived_process.returncode != 0: + if pid_exists(self.hived_process.pid): raise Exception("Error during stopping node. Manual intervention required.") + self.last_returncode = self.hived_process.returncode self.hived_process = None self.hived_lock.release() + # waits for node, to close. Recomended to use with `--exit-after-replay` flag + def wait_till_end(self): + assert self.hived_process is not None + # assert "--exit-after-replay" in self.hived_args + + from time import sleep + from psutil import pid_exists, Process, STATUS_ZOMBIE + + pid = self.hived_process.pid + while pid_exists(pid) and Process(pid).status() != STATUS_ZOMBIE: + sleep(0.25) + class HiveNodeInScreen(object): def __init__(self, hive_executable, working_dir, config_src_path, run_using_existing_data = False, node_is_steem = False): self.hive_executable = hive_executable @@ -155,9 +174,7 @@ def run_hive_node(self, additional_params = [], wait_for_blocks = True): self.hive_executable, "-d", self.working_dir, - "--advanced-benchmark", - "--sps-remove-threshold", - "-1" + "--advanced-benchmark" ] parameters = parameters + additional_params diff --git a/tests/functional/hive_utils/resources/config.ini.in b/tests/functional/hive_utils/resources/config.ini.in index b8a0aea5d3..b22f2b1bcf 100644 --- a/tests/functional/hive_utils/resources/config.ini.in +++ b/tests/functional/hive_utils/resources/config.ini.in @@ -8,7 +8,7 @@ log-logger = {"name":"default","level":"all","appender":"stderr"} {"name":"p2p", backtrace = yes # Plugin(s) to enable, may be specified multiple times -plugin = witness database_api account_by_key_api network_broadcast_api condenser_api block_api transaction_status_api debug_node_api +plugin = witness database_api account_by_key_api network_broadcast_api condenser_api block_api transaction_status_api debug_node_api account_history_rocksdb account_history_api # Disables automatic account history trimming history-disable-pruning = 0 diff --git a/tests/functional/hive_utils/resources/configini.py b/tests/functional/hive_utils/resources/configini.py new file mode 100644 index 0000000000..d95fd078fe --- /dev/null +++ b/tests/functional/hive_utils/resources/configini.py @@ -0,0 +1,118 @@ +#!/usr/bin/python3 + +# Easier way to generate configs + +class config: + def __init__(self, check_is_arg_exists : bool = True, **kwargs): + self.log_appender = '{"appender":"stderr","stream":"std_error"} {"appender":"p2p","file":"logs/p2p/p2p.log"}' + self.log_logger = '{"name":"default","level":"all","appender":"stderr"} {"name":"p2p","level":"all","appender":"p2p"}' + self.backtrace = 'yes' + self.plugin = 'witness database_api account_by_key_api network_broadcast_api condenser_api block_api transaction_status_api debug_node_api' + self.history_disable_pruning = '0' + self.account_history_rocksdb_path = '"blockchain/account-history-rocksdb-storage"' + self.block_data_export_file = 'NONE' + self.block_log_info_print_interval_seconds = '86400' + self.block_log_info_print_irreversible = '1' + self.block_log_info_print_file = 'ILOG' + self.shared_file_dir = '"blockchain"' + self.shared_file_size = '8G' + self.shared_file_full_threshold = '0' + self.shared_file_scale_rate = '0' + self.follow_max_feed_size = '500' + self.follow_start_feeds = '0' + self.market_history_bucket_size = '[15,60,300,3600,86400]' + self.market_history_buckets_per_size = '5760' + self.p2p_seed_node = '127.0.0.1:2001' + self.rc_skip_reject_not_enough_rc = '0' + self.rc_compute_historical_rc = '0' + self.statsd_batchsize = '1' + self.tags_start_promoted = '0' + self.tags_skip_startup_update = '0' + self.transaction_status_block_depth = '64000' + self.transaction_status_track_after_block = '0' + self.webserver_http_endpoint = '127.0.0.1:8090' + self.webserver_ws_endpoint = '127.0.0.1:8090' + self.webserver_thread_pool_size = '32' + self.enable_stale_production = '0' + self.required_participation = '0' + self.witness = '"initminer"' + self.private_key = '5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n' + self.witness_skip_enforce_bandwidth = '1' + self.snapshot_root_dir = '"/tmp/snapshots"' + + for key, value in kwargs.items(): + assert ( not check_is_arg_exists ) or key in self.__dict__.keys(), f"key `{key}` not found" + setattr(self, key, value) + + def generate(self, path_to_file : str): + conf = self.__dict__ + with open(path_to_file, 'w') as file: + for key, value in conf.items(): + if value is not None: + file.write("{} = {}\n".format(key.replace("_", "-"), value)) + + def load(self, path_to_file : str): + _keys = list(self.__dict__.keys()) + keys = [] + for key in _keys: + setattr(self, key, None) + keys.append(key.replace("_", "-")) + + def proc_line(line : str): + values = line.split("=") + return values[0].strip("\n\r "), values[1].strip("\n\r ") + + def match_property(line : str): + line = line.strip(" \n\r") + if line.startswith('#') or len(line) == 0: + return + key, value = proc_line(line) + if key in keys: + setattr(self, key.replace('-', '_'), value) + + with open(path_to_file, 'r') as file: + for line in file: + match_property(line) + + def update_plugins(self, required_plugins : list): + plugins = self.plugin.split(" ") + plugins.extend(x for x in required_plugins if x not in plugins) + self.plugin = " ".join(plugins) + + def exclude_plugins(self, plugins_to_exclude : list): + current_plugins = self.plugin.split(" ") + for plugin in plugins_to_exclude: + try: + current_plugins.remove(plugin) + except ValueError: + pass + self.plugin = " ".join(current_plugins) + + +def validate_address(val : str) -> bool: + try: + val = val.strip() + address, port = val.split(":") + port = int(port) + + assert port >= 0 + assert port < 0xffff + + def validate_ip(ip : list): + addr = ip.split('.') + assert len(ip) == 4 + for part in addr: + x = int(part) + assert x >= 0 + assert x <= 255 + + try: + validate_address(address) + except: + from socket import gethostbyname as get_ip + validate_address(get_ip(address)) + + return True + + except: + return False diff --git a/tests/functional/python_tests/CMakeLists.txt b/tests/functional/python_tests/CMakeLists.txt index ceaf42bc50..a444f23d7c 100644 --- a/tests/functional/python_tests/CMakeLists.txt +++ b/tests/functional/python_tests/CMakeLists.txt @@ -3,6 +3,7 @@ ENABLE_TESTING() SET ( python_funtional_tests_prefix functional ) SET ( hived_path ${CMAKE_BINARY_DIR}/programs/hived/hived ) +SET ( cli_wallet_path ${CMAKE_BINARY_DIR}/programs/cli_wallet ) SET ( initminer_key 5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n ) # It is required to start ctest in ${CMAKE_BINARY_DIR}/tests @@ -33,9 +34,12 @@ MACRO ( ADD_PYTHON_FUNCTIONAL_TEST test_script_base_name ) ADD_TEST( NAME ${test_name} COMMAND python3 ${test_parameters} WORKING_DIRECTORY ${working_dir} ) SET_PROPERTY( TEST "${test_name}" PROPERTY LABELS ${python_funtional_tests_prefix} ${tests_subset} ${test_script_base_name} ) SET_PROPERTY( TEST "${test_name}" PROPERTY ENVIRONMENT TEST_LOG_DIR=${CMAKE_BINARY_DIR}/tests) + SET_PROPERTY( TEST "${test_name}" APPEND PROPERTY ENVIRONMENT PYTHONPATH=${CMAKE_SOURCE_DIR}/tests/functional) ENDMACRO( ADD_PYTHON_FUNCTIONAL_TEST ) # Add here directories with subsets of tests ADD_SUBDIRECTORY ( dhf_tests ) ADD_SUBDIRECTORY ( hived ) +ADD_SUBDIRECTORY ( cli_wallet ) + diff --git a/tests/functional/python_tests/cli_wallet/CMakeLists.txt b/tests/functional/python_tests/cli_wallet/CMakeLists.txt new file mode 100644 index 0000000000..6e4d5c518a --- /dev/null +++ b/tests/functional/python_tests/cli_wallet/CMakeLists.txt @@ -0,0 +1,6 @@ +SET ( cli_wallet_tests_args --hive-path ${hived_path} --hive-working-dir ${CMAKE_BINARY_DIR}/tests/hive-node-data --path-to-cli ${cli_wallet_path} --creator initminer --wif ${initminer_key} --junit-output=cli_wallet_tests.xml) +IF ( BUILD_HIVE_TESTNET ) + ADD_PYTHON_FUNCTIONAL_TEST( run ${cli_wallet_tests_args} ) +ELSE ( BUILD_HIVE_TESTNET ) + MESSAGE ( STATUS "cli_wallet functional tests skipped: BUILD_HIVE_TESTNET is required" ) +ENDIF ( BUILD_HIVE_TESTNET ) diff --git a/tests/functional/python_tests/cli_wallet/__init__.py b/tests/functional/python_tests/cli_wallet/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/functional/python_tests/cli_wallet/run.py b/tests/functional/python_tests/cli_wallet/run.py index 06bcba3e39..5bdcde4609 100755 --- a/tests/functional/python_tests/cli_wallet/run.py +++ b/tests/functional/python_tests/cli_wallet/run.py @@ -5,53 +5,111 @@ import argparse import datetime import subprocess +import time +import sys +import json +from shutil import rmtree + +from junit_xml import TestCase, TestSuite from tests.utils.cmd_args import parser from tests.utils.logger import log +from tests.utils.node_util import start_node + +sys.path.append("../../") +from hive_utils.resources.configini import config test_args = [] +junit_test_cases=[] summary_file_name = "summary.txt" +test_filter=None def check_subdirs(_dir): error = False tests = sorted(glob.glob(_dir+"/*.py")) if tests: + if test_filter: + print("Tests filtered with {}".format(test_filter)) + else: + print("Tests not filtered") for test in tests: - root_error = run_script(test) - if root_error: - error = root_error + if not test_filter or test_filter in test: + root_error = run_script(test) + if root_error: + error = root_error return error +def prepare_config(args): + global test_filter + def strip_address( addr : str ): + return addr.split('/')[2] + + cfg = config() + + # required plugins + cfg.update_plugins(["account_by_key", "account_by_key_api", + "block_api", "condenser_api", "database_api", + "debug_node_api", "json_rpc", + "network_broadcast_api", "p2p", + "rc", "rc_api", "transaction_status", + "transaction_status_api", "webserver", "witness", + "market_history", "market_history_api", + "account_history_rocksdb", "account_history_api" ]) + + # option required by user + cfg.webserver_http_endpoint = strip_address(args.server_http_endpoint) + cfg.webserver_ws_endpoint = strip_address(args.server_rpc_endpoint) + + datadir = args.hive_working_dir + if not os.path.exists(datadir): + os.mkdir(datadir) + blockchain_dir = os.path.join(datadir, "blockchain") + if os.path.exists(blockchain_dir): + rmtree(blockchain_dir) + cfg.generate(os.path.join(datadir, "config.ini")) + if "test_filter" in args: + test_filter = args.test_filter def run_script(_test, _multiplier = 1, _interpreter = None ): try: with open(summary_file_name, "a+") as summary: interpreter = _interpreter if _interpreter else "python3" - ret_code = subprocess.call(interpreter + " " + _test + " " + test_args, shell=True) - if ret_code == 0: - summary.writelines("Test `{0}` passed.\n".format(_test)) - return False - else: + start_time=time.time() + out = subprocess.run(interpreter + " " + _test + " " + test_args, shell=True, stderr=subprocess.PIPE) + end_time=time.time() + test_case=TestCase(_test, _test, end_time - start_time, '', '') + junit_test_cases.append(test_case) + if out.stderr: + test_case.add_failure_info(output = out.stderr) summary.writelines("Test `{0}` failed.\n".format(_test)) return True + else: + summary.writelines("Test `{0}` passed.\n".format(_test)) + return False except Exception as _ex: log.exception("Exception occures in run_script `{0}`".format(str(_ex))) return True if __name__ == "__main__": + node = None if os.path.isfile(summary_file_name): os.remove(summary_file_name) with open(summary_file_name, "a+") as summary: summary.writelines("Cli wallet test started at {0}.\n".format(str(datetime.datetime.now())[:-7])) args = parser.parse_args() for key, val in args.__dict__.items(): - if val : + if key in ["hive_path", "hive_working_dir", "hive_config_path", "rpc_allowip", "test_filter"] or isinstance(val, bool): + continue + if val: test_args.append("--"+key.replace("_","-")+ " ") test_args.append(val) + print(test_args) test_args = " ".join(test_args) try: - error = True + prepare_config(args) + node = start_node(run_using_existing_data=True) + error = False if os.path.isdir("./tests"): error = check_subdirs("./tests") @@ -59,6 +117,12 @@ def run_script(_test, _multiplier = 1, _interpreter = None ): log.exception("Exception occured `{0}`.".format(str(_ex))) error = True finally: + if node: + node.stop_hive_node() + if args.junit_output: + test_suite = TestSuite('cli_wallet_test', junit_test_cases) + with open(args.junit_output, "w") as junit_xml: + TestSuite.to_file(junit_xml, [test_suite], prettyprint=False) if error: log.error("At least one test has faild. Please check summary.txt file.") exit(1) diff --git a/tests/functional/python_tests/cli_wallet/tests/000_create_proposal.py b/tests/functional/python_tests/cli_wallet/tests/000_create_proposal.py index 544fcfe1c4..4451b40634 100755 --- a/tests/functional/python_tests/cli_wallet/tests/000_create_proposal.py +++ b/tests/functional/python_tests/cli_wallet/tests/000_create_proposal.py @@ -2,52 +2,23 @@ import time -#from ... import utils from utils.test_utils import * from utils.cmd_args import args from utils.cli_wallet import CliWallet from utils.logger import log, init_logger if __name__ == "__main__": - try: - init_logger(__file__) + with Test(__file__): + with CliWallet( args ) as wallet: + creator, receiver = make_user_for_tests(wallet) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() + proposals_before = len(find_creator_proposals(creator, wallet.list_proposals([creator], 50, "by_creator", "ascending", "all"))) + log.info("proposals_before {0}".format(proposals_before)) - creator, receiver = make_user_for_tests(wallet) + wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") + create_prop = wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "1.000 TBD", "this is subject", "lorem", "true") - proposals_before = len(find_creator_proposals(creator, last_message_as_json( wallet.list_proposals(creator, "creator", "asc", 50, "all", "")))) - log.info("proposals_before {0}".format(proposals_before)) - - wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") - create_prop = wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "1.000 TBD", "this is subject", "lorem", "true") - - proposals_after = len(find_creator_proposals(creator, last_message_as_json( wallet.list_proposals(creator, "creator", "asc", 50, "all", "")))) - log.info("proposals_after {0}".format(proposals_after)) - - if not proposals_before + 1 == proposals_after: - raise ArgsCheckException("proposals_before +1 should be equal to proposals_after.") - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) + proposals_after = len(find_creator_proposals(creator, wallet.list_proposals([creator], 50, "by_creator", "ascending", "all"))) + log.info("proposals_after {0}".format(proposals_after)) + assert proposals_before +1 == proposals_after, "proposals_before +1 should be equal to proposals_after." diff --git a/tests/functional/python_tests/cli_wallet/tests/001_update_proposal.py b/tests/functional/python_tests/cli_wallet/tests/001_update_proposal.py deleted file mode 100755 index 13efc658cd..0000000000 --- a/tests/functional/python_tests/cli_wallet/tests/001_update_proposal.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/python3 - -import time - -from utils.test_utils import * -from utils.cmd_args import args -from utils.cli_wallet import CliWallet -from utils.logger import log, init_logger - -if __name__ == "__main__": - try: - init_logger(__file__) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - creator, receiver = make_user_for_tests(wallet) - - proposals_before = len(find_voter_proposals(creator, last_message_as_json( wallet.list_voter_proposals(creator, "creator", "asc", 20, "all", "") ))) - log.info("proposals_before {0}".format(proposals_before)) - - wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") - wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "1.000 TBD", "this is subject", "lorem", "true") - - resp = find_creator_proposals(creator, last_message_as_json(wallet.list_proposals(creator, "creator", "asc", 50, "all", ""))) - new_proposal_id = 0 - for r in resp: - if r["id"] > new_proposal_id: - new_proposal_id = r["id"] - - proposals_middle = len(find_voter_proposals(creator, last_message_as_json( wallet.list_voter_proposals(creator, "creator", "asc", 20, "all", "")))) - log.info("proposals_middle {0}".format(proposals_middle)) - - wallet.update_proposal_votes(creator, [new_proposal_id], "true", "true") - proposals_after = len(find_voter_proposals(creator, last_message_as_json( wallet.list_voter_proposals(creator, "creator", "asc", 20, "all", "") ))) - - log.info("proposals_after {0}".format(proposals_after)) - - if not proposals_before == proposals_middle: - raise ArgsCheckException("proposals_before should be equal to proposals_middle.") - - if not proposals_middle + 1 == proposals_after: - raise ArgsCheckException("proposals_middle +1 should be equal to proposals_after.") - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - diff --git a/tests/functional/python_tests/cli_wallet/tests/001_update_proposal_votes.py b/tests/functional/python_tests/cli_wallet/tests/001_update_proposal_votes.py new file mode 100755 index 0000000000..cd8f09146b --- /dev/null +++ b/tests/functional/python_tests/cli_wallet/tests/001_update_proposal_votes.py @@ -0,0 +1,34 @@ +#!/usr/bin/python3 + +from utils.test_utils import * +from utils.cmd_args import args +from utils.cli_wallet import CliWallet +from utils.logger import log, init_logger + +if __name__ == "__main__": + with Test(__file__): + with CliWallet( args ) as wallet: + creator, receiver = make_user_for_tests(wallet) + + proposals_before = len(find_voter_proposals(creator, last_message_as_json( wallet.list_proposal_votes([creator], 20, "by_voter_proposal", "ascending", "all")))) + log.info("proposals_before {0}".format(proposals_before)) + + wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") + wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "1.000 TBD", "this is subject", "lorem", "true") + + resp = find_creator_proposals(creator, last_message_as_json(wallet.list_proposals([creator], 50, "by_creator", "ascending", "all"))) + new_proposal_id = 0 + for r in resp: + if r["id"] > new_proposal_id: + new_proposal_id = r["id"] + + proposals_middle = len(find_voter_proposals(creator, last_message_as_json( wallet.list_proposal_votes([creator], 20, "by_voter_proposal", "ascending", "all")))) + log.info("proposals_middle {0}".format(proposals_middle)) + + wallet.update_proposal_votes(creator, [new_proposal_id], "true", "true") + proposals_after = len(find_voter_proposals(creator, last_message_as_json( wallet.list_proposal_votes([creator], 20, "by_voter_proposal", "ascending", "all")))) + + log.info("proposals_after {0}".format(proposals_after)) + + assert proposals_before == proposals_middle, "proposals_before should be equal to proposals_middle." + assert proposals_middle + 1 == proposals_after, "proposals_middle +1 should be equal to proposals_after." diff --git a/tests/functional/python_tests/cli_wallet/tests/002_remove_proposal.py b/tests/functional/python_tests/cli_wallet/tests/002_remove_proposal.py index 27235a7b49..a438850e88 100755 --- a/tests/functional/python_tests/cli_wallet/tests/002_remove_proposal.py +++ b/tests/functional/python_tests/cli_wallet/tests/002_remove_proposal.py @@ -1,67 +1,34 @@ #!/usr/bin/python3 -import time - from utils.test_utils import * from utils.cmd_args import args from utils.cli_wallet import CliWallet from utils.logger import log, init_logger if __name__ == "__main__": - try: - init_logger(__file__) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - creator, receiver = make_user_for_tests(wallet) - - proposals_before = len(find_creator_proposals(creator, last_message_as_json( wallet.list_proposals(creator, "creator", "asc", 20, "all", "")))) - log.info("proposals_before {0}".format(proposals_before)) - - wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") - wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "1.000 TBD", "this is subject", "lorem", "true") - - resp = find_creator_proposals(creator, last_message_as_json(wallet.list_proposals(creator, "creator", "asc", 50, "all", ""))) - new_proposal_id = 0 - for r in resp: - if r["id"] > new_proposal_id: - new_proposal_id = r["id"] - - proposals_middle = len(find_creator_proposals(creator, last_message_as_json(wallet.list_proposals(creator, "creator", "asc", 20, "all", "")))) - log.info("proposals_middle {0}".format(proposals_middle)) - - wallet.remove_proposal(creator, [new_proposal_id], "true", "true") - proposals_after = len(find_creator_proposals(creator, last_message_as_json(wallet.list_proposals(creator, "creator", "asc", 20, "all", "")))) - log.info("proposals_after {0}".format(proposals_after)) - - if not proposals_before + 1 == proposals_middle: - raise ArgsCheckException("proposals_before + 1should be equal to proposals_middle.") - - if not proposals_middle - 1 == proposals_after: - raise ArgsCheckException("proposals_middle - 1 should be equal to proposals_after.") - - if not proposals_before == proposals_after: - raise ArgsCheckException("proposals_middle - 1 should be equal to proposals_after.") - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - + with Test(__file__): + with CliWallet( args ) as wallet: + creator, receiver = make_user_for_tests(wallet) + + proposals_before = len(find_creator_proposals(creator, last_message_as_json( wallet.list_proposals([creator], 20, "by_creator", "ascending", "all")))) + log.info("proposals_before {0}".format(proposals_before)) + + wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") + wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "1.000 TBD", "this is subject", "lorem", "true") + + resp = find_creator_proposals(creator, last_message_as_json(wallet.list_proposals([creator], 50, "by_creator", "ascending", "all"))) + new_proposal_id = 0 + for r in resp: + if r["id"] > new_proposal_id: + new_proposal_id = r["id"] + + proposals_middle = len(find_creator_proposals(creator, last_message_as_json(wallet.list_proposals([creator], 20, "by_creator", "ascending", "all")))) + log.info("proposals_middle {0}".format(proposals_middle)) + + wallet.remove_proposal(creator, [new_proposal_id], "true", "true") + proposals_after = len(find_creator_proposals(creator, last_message_as_json(wallet.list_proposals([creator], 20, "by_creator", "ascending", "all")))) + log.info("proposals_after {0}".format(proposals_after)) + + assert proposals_before + 1 == proposals_middle, "proposals_before + 1should be equal to proposals_middle." + assert proposals_middle - 1 == proposals_after, "proposals_middle - 1 should be equal to proposals_after." + assert proposals_before == proposals_after, "proposals_middle - 1 should be equal to proposals_after." diff --git a/tests/functional/python_tests/cli_wallet/tests/003_list_proposal.py b/tests/functional/python_tests/cli_wallet/tests/003_list_proposal.py index c2475809ba..bb1c298438 100755 --- a/tests/functional/python_tests/cli_wallet/tests/003_list_proposal.py +++ b/tests/functional/python_tests/cli_wallet/tests/003_list_proposal.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 from collections import OrderedDict +import json from utils.test_utils import * from utils.cmd_args import args @@ -8,54 +9,32 @@ from utils.logger import log, init_logger if __name__ == "__main__": - try: - init_logger(__file__) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - active = ["active", "inactive", "all"] - order_by = ["creator", "start_date", "end_date", "total_votes"] - order_direction = ["asc", "desc"] - - for by in order_by: - for direct in order_direction: - for act in active: - if by == "creator": - start = "" - elif by == "start_date" or by == "end_date": - start = "2019-03-01T00:00:00" - else: - start = 0 - call_args = OrderedDict() - call_args["start"]=start - call_args["order_by"]=by - call_args["order_direction"]=direct - call_args["limit"]=10 - call_args["status"]=act - call_args["last_id"]="" - resp = last_message_as_json(call_and_check(wallet.list_proposals, call_args, "args")) - if not "result" in resp: - raise ArgsCheckException("No `result` in response") - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) + with Test(__file__): + with CliWallet( args ) as wallet: + active = [ "all", + "inactive", + "active", + "expired", + "votable"] + order_by = [ "by_creator", + "by_start_date", + "by_end_date", + "by_total_votes" + ] + order_type = ["ascending", + "descending"] + for by in order_by: + for direct in order_type: + for act in active: + if by == "by_start_date" or by == "by_end_date": + resp=last_message_as_json(wallet.list_proposals(["2019-03-01T00:00:00"], 10, by, direct, act)) + elif by == "by_total_votes": + resp=last_message_as_json(wallet.list_proposals([0], 10, by, direct, act)) + else: + resp=last_message_as_json(wallet.list_proposals([args.creator], 10, by, direct, act)) + if resp: + if "error" in resp: + raise ArgsCheckException("Some error occures.") + else: + raise ArgsCheckException("Parse error.") diff --git a/tests/functional/python_tests/cli_wallet/tests/004_list_voter_proposal.py b/tests/functional/python_tests/cli_wallet/tests/004_list_voter_proposal.py index 90da118f19..6366ec613d 100755 --- a/tests/functional/python_tests/cli_wallet/tests/004_list_voter_proposal.py +++ b/tests/functional/python_tests/cli_wallet/tests/004_list_voter_proposal.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 from collections import OrderedDict +import json from utils.test_utils import * from utils.cmd_args import args @@ -8,51 +9,24 @@ from utils.logger import log, init_logger if __name__ == "__main__": - try: - init_logger(__file__) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - active = ["active", "inactive", "all"] - order_by = ["creator", "start_date", "end_date", "total_votes"] - order_direction = ["asc", "desc"] - - for by in order_by: - for direct in order_direction: - for act in active: - call_args = OrderedDict() - call_args["start"]=args.creator - call_args["order_by"]=by - call_args["order_direction"]=direct - call_args["limit"]=10 - call_args["status"]=act - call_args["last_id"]="" - resp = last_message_as_json( call_and_check(wallet.list_voter_proposals, call_args, "args")) - if not "result" in resp: - raise ArgsCheckException("No `result` in response") - - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - - - + with Test(__file__): + with CliWallet( args ) as wallet: + active = [ "all", + "inactive", + "active", + "expired", + "votable"] + order_by = [ "by_voter_proposal" + ] + order_type = ["ascending", + "descending"] + + for by in order_by: + for direct in order_type: + for act in active: + resp=last_message_as_json(wallet.list_proposal_votes([args.creator], 10, by, direct, act)) + if resp: + if "error" in resp: + raise ArgsCheckException("Some error occures.") + else: + raise ArgsCheckException("Parse error.") diff --git a/tests/functional/python_tests/cli_wallet/tests/005_find_proposals.py b/tests/functional/python_tests/cli_wallet/tests/005_find_proposals.py index 64f3bb69c5..ce04ea81de 100755 --- a/tests/functional/python_tests/cli_wallet/tests/005_find_proposals.py +++ b/tests/functional/python_tests/cli_wallet/tests/005_find_proposals.py @@ -6,40 +6,15 @@ from utils.logger import log, init_logger if __name__ == "__main__": - try: - init_logger(__file__) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - id_sets = [ [1], [2], [3], [1,2], [1,2,3], [2,3], [3,4], [4,5], [1,2,3,4,5,6,7]] - - for ids in id_sets: - call_args = {"id_set":ids} - resp = last_message_as_json(call_and_check(wallet.find_proposals, call_args, "args")) - if not "result" in resp: - raise ArgsCheckException("No `result` in response") - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - - + with Test(__file__): + with CliWallet( args ) as wallet: + id_sets = [ [1], [2], [3], [1,2], [1,2,3], [2,3], [3,4], [4,5], [1,2,3,4,5,6,7]] + for ids in id_sets: + call_args = {"id_set":ids} + resp = last_message_as_json(wallet.find_proposals(ids)) + if resp: + if "error" in resp: + raise ArgsCheckException("Some error occures.") + else: + raise ArgsCheckException("Parse error.") diff --git a/tests/functional/python_tests/cli_wallet/tests/006_cli_wallet_help.py b/tests/functional/python_tests/cli_wallet/tests/006_cli_wallet_help.py index e073a74e40..d4815c6eaf 100755 --- a/tests/functional/python_tests/cli_wallet/tests/006_cli_wallet_help.py +++ b/tests/functional/python_tests/cli_wallet/tests/006_cli_wallet_help.py @@ -7,27 +7,11 @@ from utils.logger import log, init_logger if __name__ == "__main__": - try: - init_logger(__file__) - error = False + with Test(__file__): output = subprocess.run([args.path+"/cli_wallet", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) output_stdout = output.stdout.decode('utf-8') args_founded = [ arg for arg in output_stdout.split() if "--" in arg ] - only_args_to_be_founded = ['--help', '--server-rpc-endpoint', '--cert-authority', '--rpc-endpoint', '--rpc-tls-endpoint', '--rpc-tls-certificate', '--rpc-http-endpoint', '--daemon', '--rpc-http-allowip', '--wallet-file', '--chain-id'] + only_args_to_be_founded = ['--help', '--server-rpc-endpoint', '--cert-authority', '--retry-server-connection', '--rpc-endpoint', '--rpc-tls-endpoint', '--rpc-tls-certificate', '--rpc-http-endpoint', '--unlock', '--daemon', '--rpc-http-allowip', '--wallet-file', '--chain-id'] res = list(set(args_founded)^set(only_args_to_be_founded)) if res: - raise ArgsCheckException("There are some additional argument in cli_wallet `{0}`.".format(res)) - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - - - + raise ArgsCheckException("There are some additional argument in cli_wallet `{0}`.".format(res)) \ No newline at end of file diff --git a/tests/functional/python_tests/cli_wallet/tests/007_create_proposal_fail_negative_payment.py b/tests/functional/python_tests/cli_wallet/tests/007_create_proposal_fail_negative_payment.py index e6272e36c4..7d658eb788 100755 --- a/tests/functional/python_tests/cli_wallet/tests/007_create_proposal_fail_negative_payment.py +++ b/tests/functional/python_tests/cli_wallet/tests/007_create_proposal_fail_negative_payment.py @@ -9,46 +9,20 @@ from utils.logger import log, init_logger if __name__ == "__main__": - try: - init_logger(__file__) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - creator, receiver = make_user_for_tests(wallet) - - proposals_before = len(find_creator_proposals(creator, last_message_as_json( wallet.list_proposals(creator, "creator", "asc", 50, "all")))) - log.info("proposals_before {0}".format(proposals_before)) - - wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") - create_prop = wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "-1.000 TBD", "this is subject", "lorem", "true") - if not create_prop.find("daily_pay.amount >= 0: Daily pay can't be negative value") != -1: - raise ArgsCheckException("Assertion `{0}` is required.".format("daily_pay.amount >= 0: Daily pay can't be negative value")) - - proposals_after = len(find_creator_proposals(creator, last_message_as_json( wallet.list_proposals(creator, "creator", "asc", 50, "all")))) - log.info("proposals_after {0}".format(proposals_after)) - - if not proposals_before == proposals_after: - raise ArgsCheckException("proposals_before +1 should be equal to proposals_after.") - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - + with Test(__file__): + with CliWallet( args ) as wallet: + creator, receiver = make_user_for_tests(wallet) + + proposals_before = len(find_creator_proposals(creator, last_message_as_json( wallet.list_proposals([creator], 50, "by_creator", "ascending", "all")))) + log.info("proposals_before {0}".format(proposals_before)) + + wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") + create_prop = json.dumps(wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "-1.000 TBD", "this is subject", "lorem", "true")) + if not create_prop.find("daily_pay.amount >= 0: Daily pay can't be negative value") != -1: + raise ArgsCheckException("Assertion `{0}` is required.".format("daily_pay.amount >= 0: Daily pay can't be negative value")) + + proposals_after = len(find_creator_proposals(creator, last_message_as_json( wallet.list_proposals([creator], 50, "by_creator", "ascending", "all")))) + log.info("proposals_after {0}".format(proposals_after)) + + if not proposals_before == proposals_after: + raise ArgsCheckException("proposals_before +1 should be equal to proposals_after.") diff --git a/tests/functional/python_tests/cli_wallet/tests/008_delayed_voting.py b/tests/functional/python_tests/cli_wallet/tests/008_delayed_voting.py new file mode 100755 index 0000000000..35730a01cd --- /dev/null +++ b/tests/functional/python_tests/cli_wallet/tests/008_delayed_voting.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 + +import time +import json + +#from ... import utils +from utils.test_utils import * +from utils.cmd_args import args +from utils.cli_wallet import CliWallet +from utils.logger import log, init_logger + +def get_votes( wallet, account_name ): + dv_items = wallet.get_account( account_name )["result"]["delayed_votes"] + + val = 0 + for item in dv_items: + assert( "val" in item ) + val += int( item[ "val" ] ) + + return val + +if __name__ == "__main__": + with Test(__file__): + with CliWallet(args) as wallet: + creator, receiver = make_user_for_tests(wallet) + log.info( "actors: creator: {0} receiver: ".format( creator, receiver ) ) + + #=================Adding delayed votes================= + before_val = get_votes( wallet, receiver ) + + #=================Vest liquid again================= + wallet.transfer_to_vesting( creator, receiver, "1.000 TESTS", "true" ) + + #=================Adding delayed votes================= + after_val = get_votes( wallet, receiver ) + + log.info( "before_val: {0} after_val: {1}".format( before_val, after_val ) ) + + #=================Checking changes================= + assert( after_val > before_val ) diff --git a/tests/functional/python_tests/cli_wallet/tests/008_list_proposal_fail_invalid_value_for_last_id.py b/tests/functional/python_tests/cli_wallet/tests/008_list_proposal_fail_invalid_value_for_last_id.py deleted file mode 100755 index 6af4d0fac5..0000000000 --- a/tests/functional/python_tests/cli_wallet/tests/008_list_proposal_fail_invalid_value_for_last_id.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/python3 - -from utils.test_utils import * -from utils.cmd_args import args -from utils.cli_wallet import CliWallet -from utils.logger import log, init_logger - -if __name__ == "__main__": - try: - init_logger(__file__) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - error_msg_x = "The value `x` for `_last_id` argument is invalid, it should be integer type." - resp_error_x = wallet.list_proposals(args.creator, "creator", "asc", 50, "all", "x") - log.info(resp_error_x) - if resp_error_x.find(error_msg_x) == -1: - raise ArgsCheckException("Assertion `{0}` is required.".format(error_msg_x)) - - error_msg_y = "The value `y` for `_last_id` argument is invalid, it should be integer type." - resp_error_y = wallet.list_proposals(args.creator, "creator", "asc", 50, "all", "y") - log.info(resp_error_y) - if resp_error_y.find(error_msg_y) == -1: - raise ArgsCheckException("Assertion `{0}` is required.".format(error_msg_y)) - - error_msg_10 = "The value `10` for `_last_id` argument is invalid, it should be integer type." - resp_10 = wallet.list_proposals(args.creator, "creator", "asc", 50, "all", "10") - log.info(resp_10) - if resp_10.find(error_msg_10) != -1: - raise ArgsCheckException("There should be no assertion `{0}`.".format(error_msg_10)) - if not "result" in last_message_as_json(resp_10): - raise ArgsCheckException("No `result` in response") - - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - diff --git a/tests/functional/python_tests/cli_wallet/tests/009_get_open_orders.py b/tests/functional/python_tests/cli_wallet/tests/009_get_open_orders.py new file mode 100755 index 0000000000..bf62890710 --- /dev/null +++ b/tests/functional/python_tests/cli_wallet/tests/009_get_open_orders.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 + +import time + +from utils.test_utils import * +from utils.cmd_args import args +from utils.cli_wallet import CliWallet +from utils.logger import log, init_logger + +if __name__ == "__main__": + with Test(__file__): + with CliWallet( args ) as wallet: + creator, user = make_user_for_tests(wallet) + result_before = wallet.get_open_orders(user)['result'] + assert(len(result_before) == 0) + + log.info( "testing buy order :10.000 TESTS for 1000.000 TBD created by user {}".format( user ) ) + wallet.create_order(user, "1", "10.000 TESTS", "1000.000 TBD", "false", "9999", "true") + result_sell = wallet.get_open_orders(user)['result'] + assert(len(result_sell) == 1) + assert(result_sell[0]['orderid'] == 1) + assert(result_sell[0]['seller'] == user) + assert(result_sell[0]['for_sale'] == 10000) + assert(result_sell[0]['real_price'] == '100.00000000000000000') + assert(result_sell[0]['sell_price']['base'] == '10.000 TESTS') + assert(result_sell[0]['sell_price']['quote'] == '1000.000 TBD') + assert(not result_sell[0]['rewarded']) + + log.info( "testing buy order :10.000 TBD for 1000.000 TESTS created by user {}".format( user ) ) + wallet.create_order(user, "2", "10.000 TBD", "1000.000 TESTS", "false", "9999", "true") + result_buy = wallet.get_open_orders(user)['result'] + assert(len(result_buy) == 2) + assert(result_buy[1]['orderid'] == 2) + assert(result_buy[1]['seller'] == user) + assert(result_buy[1]['for_sale'] == 10000) + assert(result_buy[1]['real_price'] == '0.01000000000000000') + assert(result_buy[1]['sell_price']['base'] == '10.000 TBD') + assert(result_buy[1]['sell_price']['quote'] == '1000.000 TESTS') + assert(not result_buy[1]['rewarded']) + diff --git a/tests/functional/python_tests/cli_wallet/tests/009_list_voter_proposal_fail_invalid_value_for_last_id.py b/tests/functional/python_tests/cli_wallet/tests/009_list_voter_proposal_fail_invalid_value_for_last_id.py deleted file mode 100755 index 365e261ea2..0000000000 --- a/tests/functional/python_tests/cli_wallet/tests/009_list_voter_proposal_fail_invalid_value_for_last_id.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/python3 - -from utils.test_utils import * -from utils.cmd_args import args -from utils.cli_wallet import CliWallet -from utils.logger import log, init_logger - -if __name__ == "__main__": - try: - init_logger(__file__) - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - error_msg_x = "The value `x` for `_last_id` argument is invalid, it should be integer type." - resp_error_x = wallet.list_voter_proposals(args.creator, "creator", "asc", 50, "all", "x") - log.info(resp_error_x) - if resp_error_x.find(error_msg_x) == -1: - raise ArgsCheckException("Assertion `{0}` is required.".format(error_msg_x)) - - error_msg_y = "The value `y` for `_last_id` argument is invalid, it should be integer type." - resp_error_y = wallet.list_voter_proposals(args.creator, "creator", "asc", 50, "all", "y") - log.info(resp_error_y) - if resp_error_y.find(error_msg_y) == -1: - raise ArgsCheckException("Assertion `{0}` is required.".format(error_msg_y)) - - error_msg_10 = "The value `10` for `_last_id` argument is invalid, it should be integer type." - resp_10 = wallet.list_voter_proposals(args.creator, "creator", "asc", 50, "all", "10") - log.info(resp_10) - if resp_10.find(error_msg_10) != -1: - raise ArgsCheckException("There should be no assertion `{0}`.".format(error_msg_10)) - if not "result" in last_message_as_json(resp_10): - raise ArgsCheckException("No `result` in response") - - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - diff --git a/tests/functional/python_tests/cli_wallet/tests/010_delayed_voting.py b/tests/functional/python_tests/cli_wallet/tests/010_delayed_voting.py deleted file mode 100755 index 0ca01a7904..0000000000 --- a/tests/functional/python_tests/cli_wallet/tests/010_delayed_voting.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/python3 - -import time -import json - -#from ... import utils -from utils.test_utils import * -from utils.cmd_args import args -from utils.cli_wallet import CliWallet -from utils.logger import log, init_logger - -def get_votes( wallet, account_name ): - - pattern = "get_account" + " \"" + account_name + "\"" - - response = wallet.get_account( account_name ) - - found = response.find( pattern ) - if found == -1: - return 0 - - found += len( pattern ) - sub_response = response[ found : len( response ) ] - - items = json.loads( sub_response ) - - assert( "delayed_votes" in items ) - - dv_items = items["delayed_votes"] - - val = 0 - for item in dv_items: - assert( "val" in item ) - val += int( item[ "val" ] ) - - return val - -if __name__ == "__main__": - try: - init_logger(__file__) - - error = False - wallet = CliWallet( args.path, - args.server_rpc_endpoint, - args.cert_auth, - args.rpc_tls_endpoint, - args.rpc_tls_cert, - args.rpc_http_endpoint, - args.deamon, - args.rpc_allowip, - args.wallet_file, - args.chain_id, - args.wif ) - wallet.set_and_run_wallet() - - creator, receiver = make_user_for_tests(wallet) - log.info( "actors: creator: {0} receiver: ".format( creator, receiver ) ) - - # "delayed_votes": [{ - # "time": "2020-04-02T12:39:33", - # "val": 642832 - # } - # ] - - #=================Adding delayed votes================= - before_val = get_votes( wallet, receiver ) - - #=================Vest liquid again================= - wallet.transfer_to_vesting( creator, receiver, "1.000 TESTS", "true" ) - - #=================Adding delayed votes================= - after_val = get_votes( wallet, receiver ) - - log.info( "before_val: {0} after_val: {1}".format( before_val, after_val ) ) - - #=================Checking changes================= - assert( after_val > before_val ) - - except Exception as _ex: - log.exception(str(_ex)) - error = True - finally: - if error: - log.error("TEST `{0}` failed".format(__file__)) - exit(1) - else: - log.info("TEST `{0}` passed".format(__file__)) - exit(0) - diff --git a/tests/functional/python_tests/cli_wallet/tests/010_update_proposal.py b/tests/functional/python_tests/cli_wallet/tests/010_update_proposal.py new file mode 100755 index 0000000000..a3b1c7da41 --- /dev/null +++ b/tests/functional/python_tests/cli_wallet/tests/010_update_proposal.py @@ -0,0 +1,78 @@ +#!/usr/bin/python3 + +import time + +from utils.test_utils import * +from utils.cmd_args import args +from utils.cli_wallet import CliWallet +from utils.logger import log, init_logger + +def proposal_exists( block_number, end_date ): + delay = 66 + print("Waiting for accept reversible blocks") + time.sleep(delay) + print("End of waiting") + + output_ops = wallet.get_ops_in_block( block_number, False ) + print( output_ops ) + + if 'result' in output_ops: + _result = output_ops['result'] + if len(_result) >= 1: + if 'op' in _result[0]: + ops = _result[0]['op'] + if len(ops) == 2: + _type = ops[0] + _value = ops[1] + if 'extensions' in _value: + _extensions = _value['extensions'] + if len(_extensions) == 1: + __extensions = _extensions[0] + if len(__extensions) == 2: + _extension_type = __extensions[0] + _extension_value = __extensions[1] + if 'end_date' in _extension_value: + _end_date = _extension_value['end_date'] + if _extension_type == 1 and _end_date == end_date: + return True + return False + +if __name__ == "__main__": + with Test(__file__): + with CliWallet( args ) as wallet: + creator, receiver = make_user_for_tests(wallet) + wallet.post_comment(creator, "lorem", "", "ipsum", "Lorem Ipsum", "body", "{}", "true") + wallet.create_proposal(creator, receiver, "2029-06-02T00:00:00", "2029-08-01T00:00:00", "10.000 TBD", "this is subject", "lorem", "true") + proposals = find_creator_proposals(creator, wallet.list_proposals([creator], 50, "by_creator", "ascending", "all")) + + proposal_id = -1 + for proposal in proposals: + if proposal['receiver'] == receiver: + proposal_id = proposal['proposal_id'] + break + + assert proposal_id != -1, "Proposal just created was not found" + + log.info("testing updating the proposal with the end date") + output = wallet.update_proposal(proposal_id, creator, "9.000 TBD", "this is an updated subject", "lorem", "2029-07-25T00:00:00", "true") + + block = output['result']['ref_block_num'] + 1 + print( "block: {}".format(block) ); + check_proposal = proposal_exists( block, "2029-07-25T00:00:00" ) + assert( check_proposal ) + + proposal = wallet.find_proposals([proposal_id])['result'][0] + + assert(proposal['daily_pay'] == "9.000 TBD") + assert(proposal['subject'] == "this is an updated subject") + assert(proposal['permlink'] == "lorem") + assert(proposal['end_date'] == "2029-07-25T00:00:00") + + log.info("testing updating the proposal without the end date") + test = wallet.update_proposal(proposal_id, creator, "8.000 TBD", "this is an updated subject again", "lorem", None, "true") + proposal = wallet.find_proposals([proposal_id])['result'][0] + + assert(proposal['daily_pay'] == "8.000 TBD") + assert(proposal['subject'] == "this is an updated subject again") + assert(proposal['permlink'] == "lorem") + assert(proposal['end_date'] == "2029-07-25T00:00:00") diff --git a/tests/functional/python_tests/cli_wallet/tests/011_create_recurrent_transfer.py b/tests/functional/python_tests/cli_wallet/tests/011_create_recurrent_transfer.py new file mode 100755 index 0000000000..da5a14d93e --- /dev/null +++ b/tests/functional/python_tests/cli_wallet/tests/011_create_recurrent_transfer.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 + +import time + +from utils.test_utils import * +from utils.cmd_args import args +from utils.cli_wallet import CliWallet +from utils.logger import log, init_logger +import datetime + +if __name__ == "__main__": + with Test(__file__): + with CliWallet( args ) as wallet: + creator, receiver = make_user_for_tests(wallet) + + recurrent_transfers_before_count = len(wallet.find_recurrent_transfers(creator)['result']) + log.info("recurrent_transfers {0}".format(recurrent_transfers_before_count)) + + create_prop = wallet.recurrent_transfer(creator, receiver, "10.000 TBD", "This is a memo", "24", "6", "true") + + recurrent_transfers = wallet.find_recurrent_transfers(creator)['result'] + recurrent_transfers_after_count = len(recurrent_transfers) + log.info("recurrent_transfers_after_count {0}".format(recurrent_transfers_after_count)) + recurrent_transfer = recurrent_transfers[recurrent_transfers_after_count - 1] + + assert recurrent_transfers_before_count + 1 == recurrent_transfers_after_count, "recurrent_transfers_before_count +1 should be equal to recurrent_transfers_after_count." + assert recurrent_transfer['from'] == creator + assert recurrent_transfer['to'] == receiver + assert recurrent_transfer['amount']['amount'] == '10000' + assert recurrent_transfer['memo'] == 'This is a memo' + assert recurrent_transfer['recurrence'] == 24 + assert recurrent_transfer['consecutive_failures'] == 0 + assert recurrent_transfer['remaining_executions'] == 5 diff --git a/tests/functional/python_tests/cli_wallet/tests/__init__.py b/tests/functional/python_tests/cli_wallet/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/functional/python_tests/cli_wallet/tests/utils/cli_wallet.py b/tests/functional/python_tests/cli_wallet/tests/utils/cli_wallet.py index 8ab04696c8..ca721d02a5 100755 --- a/tests/functional/python_tests/cli_wallet/tests/utils/cli_wallet.py +++ b/tests/functional/python_tests/cli_wallet/tests/utils/cli_wallet.py @@ -7,206 +7,180 @@ import threading import subprocess -from .logger import log +from .logger import log from .cmd_args import args from .test_utils import last_message_as_json, ws_to_http class CliWalletException(Exception): - def __init__(self, _message): - self.message = _message + def __init__(self, _message): + self.message = _message - def __str__(self): - return self.message + def __str__(self): + return self.message class CliWallet(object): - class CliWalletArgs(object): - def __init__(self, _path_to_executable, - _server_rpc_endpoint, - _cert_auth, - #_rpc_endpoint, - _rpc_tls_endpoint, - _rpc_tls_cert, - _rpc_http_endpoint, - _deamon, - _rpc_allowip, - _wallet_file, - _chain_id, - _wif ): - self.path = _path_to_executable+'/cli_wallet' - self.server_rpc_endpoint = _server_rpc_endpoint - self.cert_auth = _cert_auth - #self.rpc_endpoint = _rpc_endpoint - self.rpc_tls_endpoint = _rpc_tls_endpoint - self.rpc_tls_cert = _rpc_tls_cert - self.rpc_http_endpoint = _rpc_http_endpoint - self.deamon = _deamon - self.rpc_allowip = _rpc_allowip - self.wallet_file = _wallet_file - self.chain_id = _chain_id - self.wif = _wif - - def args_to_list(self): - test_args = [] - args = {"server_rpc_endpoint": self.server_rpc_endpoint} - args["cert_auth"] = self.cert_auth - #args["rpc_endpoint"] = self.rpc_endpoint - args["rpc_tls_endpoint"] = self.rpc_tls_endpoint - args["rpc_tls_cert"] = self.rpc_tls_cert - args["rpc_http_endpoint"] =self.rpc_http_endpoint - args["deamon"] = self.deamon - args["rpc_allowip"] = self.rpc_allowip - args["wallet_file"] = self.wallet_file - args["chain_id"] = self.chain_id - for key, val in args.items(): - if val : - test_args.append("--"+key.replace("_","-")+ " ") - test_args.append(val) - test_args = " ".join(test_args) - return test_args - - - def __init__(self, _path_to_executable, - _server_rpc_endpoint="ws://127.0.0.1:8090", - _cert_auth="_default", - #_rpc_endpoint="127.0.0.1:8091", - _rpc_tls_endpoint="127.0.0.1:8092", - _rpc_tls_cert="server.pem", - _rpc_http_endpoint="127.0.0.1:8093", - _deamon=False, - _rpc_allowip=[], - _wallet_file="wallet.json", - _chain_id="18dcf0a285365fc58b71f18b3d3fec954aa0c141c44e4e5cb4cf777b9eab274e", - _wif = "5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n"): - - self.cli_args = CliWallet.CliWalletArgs(_path_to_executable, _server_rpc_endpoint, _cert_auth, #_rpc_endpoint, - _rpc_tls_endpoint, _rpc_tls_cert, - _rpc_http_endpoint, _deamon, _rpc_allowip, _wallet_file, _chain_id, _wif ) - self.cli_proc = None - self.response = "" - self.q = queue.Queue() - self.t = threading.Thread(target=self.output_reader, args=()) - - - def __getattr__(self, _method_name): - if self.cli_proc: - self.method_name = _method_name - return self - else: - log.error("Cli_wallet is not set") - raise CliWalletException("Cli_wallet is not set") - - - def __call__(self,*_args): - try: - self.response = "" - self.send_and_read(self.prepare_args(*_args)) - return self.response - except Exception as _ex: - log.exception("Exception `{0}` occuress while calling `{1}` with `{2}` args.".format(str(_ex), self.method_name, list(_args))) - - - def set_and_run_wallet(self): - try: - log.info("Calling cli_wallet with args `{0}`".format([self.cli_args.path+ " " + self.cli_args.args_to_list()])) - self.cli_proc = subprocess.Popen([self.cli_args.path+ " " + self.cli_args.args_to_list()], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, shell=True) - if not self.cli_proc: - raise CliWalletException("Failed to run cli_wallet") - self.t.daemon=True - self.t.start() - self.set_password("{0}".format("testpassword")) - self.unlock("{0}".format("testpassword")) - self.import_key("{0}".format(self.cli_args.wif)) - except Exception as _ex: - log.exception("Exception `{0}` occuress while while running cli_wallet.".format(str(_ex))) - - - #we dont have stansaction status api, so we need to combine... - def wait_for_transaction_approwal(self): - json_resp = last_message_as_json(self.response) - block_num = json_resp["result"]["block_num"] - trans_id = json_resp["result"]["id"] - url = ws_to_http(self.cli_args.server_rpc_endpoint) - idx = -1 - while True: - param = {"jsonrpc":"2.0", "method":"block_api.get_block", "params":{"block_num":block_num+idx}, "id":1} - resp = requests.post(url, json=param) - data = resp.json() - if "result" in data and "block" in data["result"]: - block_transactions = data["result"]["block"]["transaction_ids"] - if trans_id in block_transactions: - log.info("Transaction `{0}` founded in block `{1}`".format(trans_id, block_num+idx)) - break - idx += 1 - - - def check_if_transaction(self): - json_resp = last_message_as_json(self.response) - if "result" in json_resp: - if "id" in json_resp["result"]: - return True - return False - - - def read_output(self, _timeout): - while True: - try: - self.response += self.q.get(block=True, timeout=_timeout) - except queue.Empty: - break - - - def send(self, _data): - self.cli_proc.stdin.write(_data.encode("utf-8")) - self.cli_proc.stdin.flush() - - - def send_and_read(self, _data): - log.info("Sending {0}".format(_data)) - self.send(_data) - self.read_output(3) - - #asserions does not occures after above flush, so we need to send additiona `Enter` - self.send("\n") - self.read_output(0.5) - if self.check_if_transaction(): - self.wait_for_transaction_approwal() - - return self.response - - - def exit_wallet(self): - try: - if not self.cli_proc: - log.info("Cannot exit wallet, because wallet was not set - please run it first by using `run_wallet` metode.") - self.cli_proc.communicate() - return self.cli_proc - except Exception as _ex: - log.exception("Exception `{0}` occuress while while running cli_wallet.".format(str(_ex))) - - - def output_reader(self): - while True: - try: - for line in iter(self.cli_proc.stdout.readline, b''): - self.q.put_nowait(line.decode('utf-8') ) - except queue.Full: - pass - - - def prepare_args(self, *_args): - name = self.method_name - args = _args - prepared_args = name + " " - for arg in args: - if isinstance(arg, int): - prepared_args += str(arg) + " " - elif isinstance(arg, str): - if arg: - prepared_args += "\"{0}\"".format(arg) + " " - else: - prepared_args += '\"\"' + " " - else: - prepared_args += "{0}".format(arg) + " " - return prepared_args + "\n" + class CliWalletArgs(object): + def __init__(self, args ): + self.path = args.path+'/cli_wallet' + self.server_rpc_endpoint = args.server_rpc_endpoint + self.cert_auth = args.cert_auth + #self.rpc_endpoint = _rpc_endpoint + self.rpc_tls_endpoint = args.rpc_tls_endpoint + self.rpc_tls_cert = args.rpc_tls_cert + self.rpc_http_endpoint = args.rpc_http_endpoint + self.deamon = args.deamon + self.rpc_allowip = args.rpc_allowip + self.wallet_file = args.wallet_file + self.chain_id = args.chain_id + self.wif = args.wif + + def args_to_list(self): + test_args = [] + args = {"server_rpc_endpoint": self.server_rpc_endpoint} + args["cert_auth"] = self.cert_auth + #args["rpc_endpoint"] = self.rpc_endpoint + args["rpc_tls_endpoint"] = self.rpc_tls_endpoint + args["rpc_tls_cert"] = self.rpc_tls_cert + args["rpc_http_endpoint"] =self.rpc_http_endpoint + args["deamon"] = self.deamon + args["rpc_http_allowip"] = self.rpc_allowip + args["wallet_file"] = self.wallet_file + args["chain_id"] = self.chain_id + for key, val in args.items(): + if val: + if isinstance(val, list): + for _v in val: + test_args.append("--"+key.replace("_","-")+ " ") + test_args.append(_v) + continue + + if not isinstance(val, bool): + test_args.append("--"+key.replace("_","-")) + test_args.append(val) + elif key == 'deamon': + test_args.append('-d') + test_args = " ".join(test_args) + return test_args + + + def __init__(self, args): + self.cli_args = CliWallet.CliWalletArgs(args) + self.cli_proc = None + self.response = "" + + + def __getattr__(self, _method_name): + if self.cli_proc: + self.method_name = _method_name + return self + else: + log.error("Cli_wallet is not set") + raise CliWalletException("Cli_wallet is not set") + + + def __call__(self,*_args): + try: + self.response = "" + endpoint, params = self.prepare_args(*_args) + self.response = self.send_and_read(endpoint, params) + return self.response + except Exception as _ex: + log.exception("Exception `{0}` occurs while calling `{1}` with `{2}` args.".format(str(_ex), self.method_name, list(_args))) + + def __enter__(self): + self.set_and_run_wallet() + return self + + def __exit__(self, exc_type, exc_value, exc_traceback): + try: + from os import kill + kill( self.cli_proc.pid, 9 ) + except: + self.cli_proc.send_signal( subprocess.signal.SIGTERM ) # send ^C + self.cli_proc.send_signal( subprocess.signal.SIGKILL ) # terminate + finally: + log.info("wallet closed") + if exc_type: + log.error(f"Error: {exc_value}") + log.info(f"Traceback: {exc_traceback}") + + def set_and_run_wallet(self): + from shlex import split as argsplitter + try: + print("Calling cli_wallet with args `{0}`".format([self.cli_args.path+ " " + self.cli_args.args_to_list()])) + self.cli_proc = subprocess.Popen( args=[self.cli_args.path, *argsplitter(self.cli_args.args_to_list())], stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL , stdout=subprocess.DEVNULL ) + if not self.cli_proc: + raise CliWalletException("Failed to run cli_wallet") + + from time import sleep + sleep(2) + + self.set_password("{0}".format("testpassword")) + self.unlock("{0}".format("testpassword")) + self.import_key("{0}".format(self.cli_args.wif)) + except Exception as _ex: + log.exception("Exception `{0}` occuress while while running cli_wallet.".format(str(_ex))) + + + #we dont have stansaction status api, so we need to combine... + def wait_for_transaction_approwal(self): + json_resp = last_message_as_json(self.response) + block_num = json_resp["result"]["block_num"] + trans_id = json_resp["result"]["id"] + url = ws_to_http(self.cli_args.server_rpc_endpoint) + idx = -1 + while True: + param = {"jsonrpc":"2.0", "method":"block_api.get_block", "params":{"block_num":block_num+idx}, "id":1} + resp = requests.post(url, json=param) + data = resp.json() + if "result" in data and "block" in data["result"]: + block_transactions = data["result"]["block"]["transaction_ids"] + if trans_id in block_transactions: + log.info("Transaction `{0}` founded in block `{1}`".format(trans_id, block_num+idx)) + break + idx += 1 + + + def check_if_transaction(self): + json_resp = last_message_as_json(self.response) + if "result" in json_resp: + if "id" in json_resp["result"]: + return True + return False + + def send(self, _data): + self.cli_proc.stdin.write(_data.encode("utf-8")) + self.cli_proc.stdin.flush() + + + def send_and_read(self, _endpoint, _data): + data = { + "jsonrpc": "2.0", + "method": _endpoint, + "params": _data, + "id": 1 + } + data = json.dumps( data ) + + self.response = requests.post( + f'http://{self.cli_args.rpc_http_endpoint}', + data=data + ).json() + + return self.response + + + def exit_wallet(self): + try: + if not self.cli_proc: + log.info("Cannot exit wallet, because wallet was not set - please run it first by using `run_wallet` metode.") + self.cli_proc.communicate() + return self.cli_proc + except Exception as _ex: + log.exception("Exception `{0}` occuress while while running cli_wallet.".format(str(_ex))) + + def prepare_args(self, *_args): + from json import dumps + return self.method_name, [ arg for arg in _args ] diff --git a/tests/functional/python_tests/cli_wallet/tests/utils/cmd_args.py b/tests/functional/python_tests/cli_wallet/tests/utils/cmd_args.py old mode 100644 new mode 100755 index 1b8cc59c41..9c0c738c84 --- a/tests/functional/python_tests/cli_wallet/tests/utils/cmd_args.py +++ b/tests/functional/python_tests/cli_wallet/tests/utils/cmd_args.py @@ -4,18 +4,23 @@ parser = argparse.ArgumentParser(description='Hived cli wallet test args.') parser.add_argument('--path-to-cli' , dest='path' , help ='Path to cli_wallet executable') -parser.add_argument('--creator' , dest='creator' , help ='Account to create proposals with') +parser.add_argument('--creator' , dest='creator' , help ='Account to create proposals with', default="initminer") parser.add_argument('--wif' , dest='wif' , help ='Private key for creator account', default ="5JNHfZYKGaomSFvd4NUdQ9qMcEAC43kujbfjueTHpVapX1Kzq2n") -parser.add_argument('--server-rpc-endpoint', dest="server_rpc_endpoint", help = "Set server endpoint [=ws://127.0.0.1:8090]", default ="ws://127.0.0.1:8090") -parser.add_argument('--cert-auth' , dest="cert_auth" , help = "Set cert auth [=_default]" , default ="_default") +parser.add_argument('--server-rpc-endpoint', dest="server_rpc_endpoint", help = "Set server endpoint [=ws://127.0.0.1:8090]", default = "ws://127.0.0.1:8090") +parser.add_argument('--server-http-endpoint', dest="server_http_endpoint", help = "Set server endpoint [=http://127.0.0.1:8091]", default = "http://127.0.0.1:8091") +parser.add_argument('--cert-auth' , dest="cert_auth" , help = "Set cert auth" , default = None) #this argument causes error #parser.add_argument('--rpc-endpoint' , dest="rpc_endpoint" , help = "Set rpc endpoint [=127.0.0.1:8091]" , default ="127.0.0.1:8091") -parser.add_argument('--rpc-tls-endpoint' , dest="rpc_tls_endpoint" , help = "Set rpc tle endpont [=127.0.0.1:8092]" , default ="127.0.0.1:8092") -parser.add_argument('--rpc-tls-cert' , dest="rpc_tls_cert" , help = "Set rpc tls cert [=server.pem]" , default ="server.pem") -parser.add_argument('--rpc-http-endpoint' , dest="rpc_http_endpoint" , help = "Set rpc http endpoint [=127.0.0.1:8093]" , default ="127.0.0.1:8093") -parser.add_argument('--deamon' , dest="deamon" , help = "Set to work as deamon [=False]" , default =False) -parser.add_argument('--rpc-allowip' , dest="rpc_allowip" , help = "Set allowed rpc ip [=[]]" , default =[]) +parser.add_argument('--rpc-tls-endpoint' , dest="rpc_tls_endpoint" , help = "Set rpc tle endpont" , default = None) +parser.add_argument('--rpc-tls-cert' , dest="rpc_tls_cert" , help = "Set rpc tls cert" , default = None) +parser.add_argument('--rpc-http-endpoint' , dest="rpc_http_endpoint" , help = "Set rpc http endpoint [='127.0.0.1:8093']" , default = "127.0.0.1:8093") +parser.add_argument('--deamon' , dest="deamon" , help = "Set to work as deamon" , default =True) +parser.add_argument('--rpc-allowip' , dest="rpc_allowip" , help = "Set allowed rpc ip [=['127.0.0.1']]" , default =['127.0.0.1']) parser.add_argument('--wallet-file' , dest="wallet_file" , help = "Set wallet name [=wallet.json]" , default ="wallet.json") -parser.add_argument('--chain-id' , dest="chain_id" , help = "Set chain id [=18dcf0a285365fc58b71f18b3d3fec954aa0c141c44e4e5cb4cf777b9eab274e]", default ="18dcf0a285365fc58b71f18b3d3fec954aa0c141c44e4e5cb4cf777b9eab274e") - +parser.add_argument('--chain-id' , dest="chain_id" , help = "Set chain id", default = None) +parser.add_argument("--hive-path" , dest="hive_path" , help = "Path to hived executable. Warning: using this option will erase contents of selected hived working directory.", default="") +parser.add_argument("--hive-working-dir" , dest="hive_working_dir" , default="/tmp/hived-data/", help = "Path to hived working directory") +parser.add_argument("--hive-config-path" , dest="hive_config_path" , default="../../hive_utils/resources/config.ini.in",help = "Path to source config.ini file") +parser.add_argument("--junit-output" , dest="junit_output" , default=None, help="Filename for generating jUnit-compatible XML output") +parser.add_argument("--test-filter" , dest="test_filter" , default=None, help="Substring for filtering tests by name") args = parser.parse_args() \ No newline at end of file diff --git a/tests/functional/python_tests/cli_wallet/tests/utils/logger.py b/tests/functional/python_tests/cli_wallet/tests/utils/logger.py index ac5ef90b74..a5f1393a93 100644 --- a/tests/functional/python_tests/cli_wallet/tests/utils/logger.py +++ b/tests/functional/python_tests/cli_wallet/tests/utils/logger.py @@ -15,18 +15,15 @@ def init_logger(_file_name): log.setLevel(logging.INFO) data = os.path.split(_file_name) - if data[0]: - path = data[0] + "/logs/" + log_dir = os.environ.get("TEST_LOG_DIR", None) + if log_dir: + path = log_dir else: - path = "./logs/" - file = data[1] + path = data[0] + "/logs/" + file_name = "cli_wallet.log" if path and not os.path.exists(path): os.makedirs(path) - now = str(datetime.datetime.now())[:-7] - now = now.replace(' ', '-') - full_path = path+"/"+now+"_"+file - if not full_path.endswith(".log"): - full_path += (".log") + full_path = path+"/"+file_name fileh = logging.FileHandler(full_path) fileh.setFormatter(logging.Formatter(formater)) log.addHandler(fileh) diff --git a/tests/functional/python_tests/cli_wallet/tests/utils/node_util.py b/tests/functional/python_tests/cli_wallet/tests/utils/node_util.py new file mode 100644 index 0000000000..97c40a290b --- /dev/null +++ b/tests/functional/python_tests/cli_wallet/tests/utils/node_util.py @@ -0,0 +1,23 @@ +import sys +sys.path.append("../../../../") +from tests.utils.cmd_args import args +from tests.utils.logger import log +sys.path.append("../../") +import hive_utils + + +def start_node(**kwargs): + node=None + if args.hive_path: + log.info("Running hived via {} in {} with config {}".format(args.hive_path, + args.hive_working_dir, + args.hive_config_path) + ) + + node = hive_utils.hive_node.HiveNodeInScreen( + args.hive_path, + args.hive_working_dir, + args.hive_config_path, **kwargs + ) + node.run_hive_node(["--enable-stale-production"]) + return node \ No newline at end of file diff --git a/tests/functional/python_tests/cli_wallet/tests/utils/test_utils.py b/tests/functional/python_tests/cli_wallet/tests/utils/test_utils.py index 5f698ea422..7291b24ee2 100644 --- a/tests/functional/python_tests/cli_wallet/tests/utils/test_utils.py +++ b/tests/functional/python_tests/cli_wallet/tests/utils/test_utils.py @@ -1,7 +1,12 @@ import json import requests +from contextlib import ContextDecorator +import traceback +import time -from utils.logger import log +from junit_xml import TestCase, TestSuite + +from utils.logger import log, init_logger from utils.cmd_args import args class ArgsCheckException(Exception): @@ -11,9 +16,37 @@ def __init__(self, _message): def __str__(self): return self.message +class Asset: + def __init__( self, am : float, dec : int = None, sym : str = None ): + if dec is None and sym is None: + am, dec, sym = self.__parse_str(am) + + self.amount = am + self.decimals = dec + self.symbol = sym + + def to_string(self): + return "{1:.{0}f} {2}".format(self.decimals, self.amount, self.symbol) + + # returns amount, decimals and symbol + def __parse_str( self, _in : str ): + _in = _in.strip() + amount, symbol = _in.split(' ') + _, decimals = amount.split('.') + amount = float(amount) + return amount, len(decimals), symbol + + def __str__(self): + return self.to_string() + + def satoshi(self): + return Asset( 10 ** ( -1 * 3 ), self.decimals, self.symbol ) + user_name = list("aaaaaaaaaaaa") creator = "initminer" + + def unifie_to_string(_arg): if not isinstance(_arg, str): prepared = str(_arg) @@ -57,42 +90,48 @@ def call_and_check_transaction(_func, _call_args, _arg_prefix, _broadcast): def last_message_as_json( _message): - if "message:" in _message: - _message = _message[_message.rfind("message:")+len("message:"):] - _message.strip() - o = 0 - #lame... but works - for index, ch in enumerate(_message): - if str(ch) == "{": - o +=1 - continue - if str(ch) == "}": - o -=1 - if o == 0: - _message = _message[:index+1] - break - else: - _message = "{}" - return json.loads(_message) + if isinstance(_message, dict): + return _message + log.info(_message) + if "message:" in _message: + _message = _message[_message.rfind("message:")+len("message:"):] + _message.strip() + o = 0 + #lame... but works + for index, ch in enumerate(_message): + if str(ch) == "{": + o +=1 + continue + if str(ch) == "}": + o -=1 + if o == 0: + _message = _message[:index+1] + break + else: + _message = "{}" + return json.loads(_message) def find_creator_proposals(_creator, _proposal_list): proposals = [] if "result" in _proposal_list: result = _proposal_list["result"] - for rs in result: - if rs["creator"] == _creator: - proposals.append(rs) + if result: + for rs in result: + if rs["creator"] == _creator: + proposals.append(rs) return proposals def find_voter_proposals(_voter, _proposal_list): + proposals = [] if "result" in _proposal_list: result = _proposal_list["result"] - for user, user_propsals in result.items(): - if user == _voter: - return user_propsals - return [] + if result: + for rs in result: + if rs["voter"] == _voter: + proposals.append(rs) + return proposals def ws_to_http(_url): @@ -101,7 +140,7 @@ def ws_to_http(_url): def get_valid_hive_account_name(): - http_url = ws_to_http(args.server_rpc_endpoint) + http_url = args.server_http_endpoint while True: params = {"jsonrpc":"2.0", "method":"condenser_api.get_accounts", "params":[["".join(user_name)]], "id":1} resp = requests.post(http_url, json=params) @@ -121,17 +160,63 @@ def get_valid_hive_account_name(): if len(set(user_name)) == 1 and user_name[0] == 'z': break -def make_user_for_tests(_cli_wallet, _value_for_vesting = None, _value_for_transfer_tests = None, _value_for_transfer_tbd = None): - value_for_vesting = _value_for_vesting if _value_for_vesting else "20.000 TESTS" - value_for_transfer_tests = _value_for_transfer_tests if _value_for_transfer_tests else "20.000 TESTS" - value_for_transfer_tbd = _value_for_transfer_tbd if _value_for_transfer_tbd else "20.000 TBD" - +# if all_diffrent: { "owner": {"pub", "prv"}, "active": {"pub", "prv"}, "posting": {"pub", "prv"}, "memo": {"pub", "prv"} } +# if not all_diffrent: {"pub", "prv"} +def get_keys( cli_wallet, import_to_wallet : bool = True, all_diffrent : bool = False) -> dict: + def _get_prv_pub_pair() -> dict: + keys = cli_wallet.suggest_brain_key()['result'] + if import_to_wallet: + cli_wallet.import_key( keys['wif_priv_key'] ) + return { "pub": keys['pub_key'], "prv": keys['wif_priv_key'] } + + if all_diffrent: + return { + "owner": _get_prv_pub_pair(), + "active": _get_prv_pub_pair(), + "posting": _get_prv_pub_pair(), + "memo": _get_prv_pub_pair() + } + else: + return _get_prv_pub_pair() + +def make_user_for_tests(_cli_wallet, _value_for_vesting = "20.000 TESTS", _value_for_transfer_tests = "20.000 TESTS", _value_for_transfer_tbd = "20.000 TBD"): receiver = get_valid_hive_account_name() - _cli_wallet.create_account( creator, receiver, "{}", "true") + print(_cli_wallet.create_account( creator, receiver, "{}", "true")) - _cli_wallet.transfer_to_vesting( creator, receiver, value_for_vesting, "true") - _cli_wallet.transfer( creator, receiver, value_for_transfer_tests, "initial transfer", "true" ) - _cli_wallet.transfer( creator, receiver, value_for_transfer_tbd, "initial transfer", "true") + _cli_wallet.transfer_to_vesting( creator, receiver, _value_for_vesting, "true") + _cli_wallet.transfer( creator, receiver, _value_for_transfer_tests, "initial transfer", "true" ) + _cli_wallet.transfer( creator, receiver, _value_for_transfer_tbd, "initial transfer", "true") return creator, receiver + +def create_users( _cli_wallet, count ) -> dict: + ret = dict() + for _ in range(count): + name = get_valid_hive_account_name() + keys = get_keys( _cli_wallet ) + pub = keys['pub'] + ret[name] = keys + _cli_wallet.create_account_with_keys( creator, name, "{}", pub, pub, pub, pub, "true" ) + return ret + +class Test(ContextDecorator): + def __init__(self, _test_name): + self.test_name=_test_name + + def __enter__(self): + init_logger(self.test_name) + log.info("Starting test: {0}".format(self.test_name)) + + def __exit__(self, exc_type, exc_value, exc_traceback): + self.error=None + if exc_type: + log.exception(exc_value) + self.error = traceback.format_exception(exc_type, exc_value, exc_traceback) + if self.error: + log.error("TEST `{0}` failed".format(self.test_name)) + raise Exception(str(self.error)) + else: + log.info("TEST `{0}` passed".format(self.test_name)) + exit(0) + diff --git a/tests/functional/python_tests/create_account_tests/.gitignore b/tests/functional/python_tests/create_account_tests/.gitignore new file mode 100644 index 0000000000..a07eae7fa3 --- /dev/null +++ b/tests/functional/python_tests/create_account_tests/.gitignore @@ -0,0 +1 @@ +GeneratedInWorld/ diff --git a/tests/functional/python_tests/create_account_tests/pytest.ini b/tests/functional/python_tests/create_account_tests/pytest.ini new file mode 100644 index 0000000000..0c84ee30f9 --- /dev/null +++ b/tests/functional/python_tests/create_account_tests/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +filterwarnings = error diff --git a/tests/functional/python_tests/create_account_tests/test_getting_key_references_of_claimed_created_account.py b/tests/functional/python_tests/create_account_tests/test_getting_key_references_of_claimed_created_account.py new file mode 100644 index 0000000000..61028d9611 --- /dev/null +++ b/tests/functional/python_tests/create_account_tests/test_getting_key_references_of_claimed_created_account.py @@ -0,0 +1,26 @@ +# This is a regression test for problem described in following issue: +# https://gitlab.syncad.com/hive/hive/-/issues/113 + +from test_tools import Account, logger, World + + +def test_getting_key_references_of_claimed_created_account(): + with World() as world: + init_node = world.create_init_node() + init_node.config.plugin.append('network_broadcast_api') + init_node.run() + + wallet = init_node.attach_wallet() + + logger.info('Waiting until initminer will be able to create account...') + init_node.wait_number_of_blocks(30) + + account = Account('alice') + key = account.public_key + wallet.api.claim_account_creation('initminer', '0.000 TESTS') + wallet.api.create_funded_account_with_keys( + 'initminer', account.name, '0.000 TESTS', 'memo', '{}', key, key, key, key + ) + + response = init_node.api.condenser.get_key_references([account.public_key]) + assert response['result'] == [[account.name]] diff --git a/tests/functional/python_tests/dhf_tests/beem_dhf_tests.py b/tests/functional/python_tests/dhf_tests/beem_dhf_tests.py index a02824c634..289d1ce085 100755 --- a/tests/functional/python_tests/dhf_tests/beem_dhf_tests.py +++ b/tests/functional/python_tests/dhf_tests/beem_dhf_tests.py @@ -358,17 +358,21 @@ def test_iterate_results_test(node, creator_account, receiver_account, wif, subj @junit_test_case def test_update_proposal(node, creator, wif): - logger.info("Testing: update_proposal") + from beembase.operations import Update_proposal + from datetime import timedelta + import dateutil.parser s = Hive(node = [node], no_broadcast = False, keys = [wif]) + + logger.info("Testing: update_proposal without updating the end date") proposals = s.rpc.list_proposals([creator], 1000, "by_creator", "ascending", "all") print(proposals[0]) - from beembase.operations import Update_proposal - subject = "Some new proposal subject" + new_subject = "Some new proposal subject" + new_daily_pay = "15.000 TBD" op = Update_proposal(**{ 'proposal_id' : proposals[0]["proposal_id"], 'creator' : proposals[0]["creator"], - 'daily_pay' : "16.000 TBD", - 'subject' : subject, + 'daily_pay' : new_daily_pay, + 'subject' : new_subject, 'permlink': proposals[0]["permlink"] }) try: @@ -380,7 +384,31 @@ def test_update_proposal(node, creator, wif): proposals = s.rpc.list_proposals([creator], 1000, "by_creator", "ascending", "all") print(proposals[0]) - assert proposals[0]["subject"] == subject, "Subjects dont match" + assert proposals[0]["subject"] == new_subject, "Subjects dont match" + assert proposals[0]["daily_pay"] == new_daily_pay, "daily pay dont match" + + logger.info("Testing: update_proposal and updating the end date") + end_date = test_utils.date_to_iso(dateutil.parser.parse(proposals[0]['end_date']) - timedelta(days=1)) + + op = Update_proposal(**{ + 'proposal_id' : proposals[0]["proposal_id"], + 'creator' : proposals[0]["creator"], + 'daily_pay' : "15.000 TBD", + 'subject' : new_subject, + 'prefix' : "TST", + 'permlink': proposals[0]["permlink"], + 'end_date': end_date + }) + try: + s.finalizeOp(op, creator, "active") + except Exception as ex: + logger.exception("Exception: {}".format(ex)) + raise ex + hive_utils.common.wait_n_blocks(node, 3) + + proposals = s.rpc.list_proposals([creator], 1000, "by_creator", "ascending", "all") + print(proposals[0]) + assert proposals[0]["end_date"] == end_date, "End date doesn't match" if __name__ == '__main__': logger.info("Performing SPS tests") @@ -442,7 +470,7 @@ def test_update_proposal(node, creator, wif): sys.exit(1) except Exception as ex: logger.error("Exception: {}".format(ex)) - if node is not None: + if node is not None: node.stop_hive_node() finally: if args.junit_output is not None: diff --git a/tests/functional/python_tests/dhf_tests/list_voter_proposal_sort.py b/tests/functional/python_tests/dhf_tests/list_voter_proposal_sort.py index 709a979bc2..96aee8d815 100644 --- a/tests/functional/python_tests/dhf_tests/list_voter_proposal_sort.py +++ b/tests/functional/python_tests/dhf_tests/list_voter_proposal_sort.py @@ -268,9 +268,8 @@ def create_proposals(node, accounts, start_date, end_date, wif=None): hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, end_date_blocks_str, False) logger.info("Balances for accounts at time: {}".format(end_date_blocks_str)) balances = test_utils.print_balance(node_client, accounts) - #should be 438.000, 437.904 becouse of rounding for balance in balances: - assert balance == '437904', "All balances should be equal 437904" + assert balance == '438000', "All balances should be equal 438000" test_utils.print_balance(node_client, [{'name' : args.treasury}]) votes = test_utils.list_proposals(node_client, start_date_str, "expired") for vote in votes: diff --git a/tests/functional/python_tests/dhf_tests/proposal_payment_test_001.py b/tests/functional/python_tests/dhf_tests/proposal_payment_test_001.py index e05e642387..7dbde7c661 100644 --- a/tests/functional/python_tests/dhf_tests/proposal_payment_test_001.py +++ b/tests/functional/python_tests/dhf_tests/proposal_payment_test_001.py @@ -141,7 +141,7 @@ def create_proposals(node, accounts, start_date, end_date, wif=None): node.run_hive_node(["--enable-stale-production"]) try: if node is None or node.is_running(): - node_client = Hive(node = [node_url], no_broadcast = False, + node_client = Hive(node = [node_url], no_broadcast = False, keys = keys ) @@ -151,21 +151,19 @@ def create_proposals(node, accounts, start_date, end_date, wif=None): # create accounts test_utils.create_accounts(node_client, args.creator, accounts) # tranfer to vesting - test_utils.transfer_to_vesting(node_client, args.creator, accounts, "300.000", - "TESTS" - ) + test_utils.transfer_to_vesting(node_client, args.creator, accounts, "300.000", "TESTS") logger.info("Wait 30 days for full voting power") hive_utils.debug_quick_block_skip(node_client, wif, (30 * 24 * 3600 / 3)) hive_utils.debug_generate_blocks(node_client.rpc.url, wif, 10) # transfer assets to accounts - test_utils.transfer_assets_to_accounts(node_client, args.creator, accounts, + test_utils.transfer_assets_to_accounts(node_client, args.creator, accounts, "400.000", "TESTS", wif ) - test_utils.transfer_assets_to_accounts(node_client, args.creator, accounts, + test_utils.transfer_assets_to_accounts(node_client, args.creator, accounts, "400.000", "TBD", wif ) @@ -173,7 +171,7 @@ def create_proposals(node, accounts, start_date, end_date, wif=None): logger.info("Balances for accounts after initial transfer") test_utils.print_balance(node_client, accounts) # transfer assets to treasury - test_utils.transfer_assets_to_treasury(node_client, args.creator, args.treasury, + test_utils.transfer_assets_to_treasury(node_client, args.creator, args.treasury, "1000000.000", "TBD", wif ) test_utils.print_balance(node_client, [{'name' : args.treasury}]) @@ -245,9 +243,8 @@ def create_proposals(node, accounts, start_date, end_date, wif=None): hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, end_date_blocks_str, False) logger.info("Balances for accounts at time: {}".format(end_date_blocks_str)) balances = test_utils.print_balance(node_client, accounts) - #should be 438.000, 437.904 becouse of rounding for balance in balances: - assert balance == '437904', "All balances should be equal 437904" + assert balance == '438000', "All balances should be equal 438000" test_utils.print_balance(node_client, [{'name' : args.treasury}]) votes = test_utils.list_proposals(node_client, start_date_str, "expired") for vote in votes: @@ -260,6 +257,6 @@ def create_proposals(node, accounts, start_date, end_date, wif=None): sys.exit(1) except Exception as ex: logger.error("Exception: {}".format(ex)) - if node is not None: + if node is not None: node.stop_hive_node() sys.exit(1) diff --git a/tests/functional/python_tests/dhf_tests/proposal_payment_test_002.py b/tests/functional/python_tests/dhf_tests/proposal_payment_test_002.py index ab5752cbb9..de939201e4 100644 --- a/tests/functional/python_tests/dhf_tests/proposal_payment_test_002.py +++ b/tests/functional/python_tests/dhf_tests/proposal_payment_test_002.py @@ -152,10 +152,10 @@ now = test_utils.date_from_iso(now) proposal_data = [ - ['tester001', 1 + 0, 5, '24.000 TBD'], # starts one day from now and lasts five days + ['tester001', 1 + 0, 3, '24.000 TBD'], # starts one day from now and lasts three days ['tester002', 1 + 0, 2, '24.000 TBD'], # starts one day from now and lasts two days - ['tester003', 1 + 2, 1, '24.000 TBD'], # starts three days from now and lasts one day - ['tester004', 1 + 4, 1, '24.000 TBD'] # starts four days from now and lasts one day + ['tester003', 1 + 1, 1, '24.000 TBD'], # starts two days from now and lasts one day + ['tester004', 1 + 2, 1, '24.000 TBD'] # starts three days from now and lasts one day ] proposals = [ @@ -172,7 +172,7 @@ test_start_date = now + datetime.timedelta(days = 1) test_start_date_iso = test_utils.date_to_iso(test_start_date) - test_end_date = test_start_date + datetime.timedelta(days = 6, hours = 1) + test_end_date = test_start_date + datetime.timedelta(days = 4, hours = 1) test_end_date_iso = test_utils.date_to_iso(test_end_date) test_utils.create_proposals(node_client, proposals, wif) @@ -222,14 +222,11 @@ hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, test_end_date_iso, False) logger.info("Balances for accounts at time: {}".format(test_end_date_iso)) balances = test_utils.print_balance(node_client, accounts) - # should be '510.000 TBD', '438.000 TBD', '414.000 TBD', '414.000 TBD', - # but because of "rounding" implementation it is - # 509.760 TBD, 437.904 TBD, 413.952 TBD, 413.952 TBD test_balances = [ - '509760', - '437904', - '413952', - '413952', + '462000', + '438000', + '414000', + '414000', ] for idx in range(0, len(test_balances)): diff --git a/tests/functional/python_tests/dhf_tests/proposal_payment_test_004.py b/tests/functional/python_tests/dhf_tests/proposal_payment_test_004.py index 9f9df9921d..5e1c59e48f 100644 --- a/tests/functional/python_tests/dhf_tests/proposal_payment_test_004.py +++ b/tests/functional/python_tests/dhf_tests/proposal_payment_test_004.py @@ -199,13 +199,13 @@ # list proposals with inactive status, it shoud be list of pairs id:total_votes votes = test_utils.list_proposals(node_client, test_start_date_iso, "inactive") for vote in votes: - #should be 0 for all + # should be 0 for all assert vote == 0, "All votes should be equal to 0" logger.info("Balances for accounts after creating proposals") balances = test_utils.print_balance(node_client, accounts) for balance in balances: - #should be 390.000 TBD for all + # should be 390.000 TBD for all assert balance == '390000', "All balances should be equal to 390.000 TBD" test_utils.print_balance(node_client, [{'name' : args.treasury}]) diff --git a/tests/functional/python_tests/dhf_tests/proposal_payment_test_007.py b/tests/functional/python_tests/dhf_tests/proposal_payment_test_007.py index d6153792c8..cc059bf8ae 100644 --- a/tests/functional/python_tests/dhf_tests/proposal_payment_test_007.py +++ b/tests/functional/python_tests/dhf_tests/proposal_payment_test_007.py @@ -260,13 +260,11 @@ def unvote_proposals(node, accounts, debug_key): hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, test_end_date_iso, False) logger.info("Balances for accounts at time: {}".format(test_end_date_iso)) balances = test_utils.print_balance(node_client, accounts) - # is should be '463.000 TBD', '486.000 TBD', '486.000 TBD', '486.000 TBD', - # but because of "rounding" implementation it is 462.854 TBD,485.808 TBD,485.808 TBD,485.808 TBD test_balances = [ - '462854', - '485808', - '485808', - '485808', + '463000', + '486000', + '486000', + '486000', ] for idx in range(0, len(test_balances)): assert balances[idx] == test_balances[idx], "Balances dont match {} != {}".format(balances[idx], test_balances[idx]) diff --git a/tests/functional/python_tests/dhf_tests/proposal_payment_test_008.py b/tests/functional/python_tests/dhf_tests/proposal_payment_test_008.py index 21d152d737..9aa28a9d1a 100644 --- a/tests/functional/python_tests/dhf_tests/proposal_payment_test_008.py +++ b/tests/functional/python_tests/dhf_tests/proposal_payment_test_008.py @@ -250,13 +250,11 @@ def vote_proposals(node, accounts, wif): hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, test_end_date_iso, False) logger.info("Balances for accounts at time: {}".format(test_end_date_iso)) balances = test_utils.print_balance(node_client, accounts) - # it should be '476.000 TBD', '486.000 TBD', '486.000 TBD', '486.000 TBD', - # but because of rounding implementation it is 475.808 TBD,485.808 TBD,485.808 TBD,485.808 TBD test_balances = [ - '475808', - '485808', - '485808', - '485808', + '476000', + '486000', + '486000', + '486000', ] for idx in range(0, len(test_balances)): assert balances[idx] == test_balances[idx], "Balances dont match {} != {}".format(balances[idx], test_balances[idx]) diff --git a/tests/functional/python_tests/dhf_tests/proposal_payment_test_009.py b/tests/functional/python_tests/dhf_tests/proposal_payment_test_009.py index afe0a0c09c..2ae6557f16 100644 --- a/tests/functional/python_tests/dhf_tests/proposal_payment_test_009.py +++ b/tests/functional/python_tests/dhf_tests/proposal_payment_test_009.py @@ -258,13 +258,11 @@ def vote_proposals(node, accounts, wif): hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, test_end_date_iso, False) logger.info("Balances for accounts at time: {}".format(test_end_date_iso)) balances = test_utils.print_balance(node_client, accounts) - # it should be '390.000 TBD', '486.000 TBD', '486.000 TBD', '486.000 TBD', - # but because of rounding implementation it is 390.000 TBD,485.808 TBD,485.808 TBD,485.808 TBD test_balances = [ '390000', - '485808', - '485808', - '485808', + '486000', + '486000', + '486000', ] for idx in range(0, len(test_balances)): assert balances[idx] == test_balances[idx], "Balances dont match {} != {}".format(balances[idx], test_balances[idx]) diff --git a/tests/functional/python_tests/dhf_tests/proposal_payment_test_with_governance_010.py b/tests/functional/python_tests/dhf_tests/proposal_payment_test_with_governance_010.py new file mode 100644 index 0000000000..a20d7b1979 --- /dev/null +++ b/tests/functional/python_tests/dhf_tests/proposal_payment_test_with_governance_010.py @@ -0,0 +1,249 @@ +#!/usr/bin/python3 + +import sys +sys.path.append("../../") +import hive_utils + +from uuid import uuid4 +from time import sleep +import logging +import test_utils +import os + +LOG_LEVEL = logging.INFO +LOG_FORMAT = "%(asctime)-15s - %(name)s - %(levelname)s - %(message)s" +MAIN_LOG_PATH = "hdf_proposal_payment_002.log" +log_dir = os.environ.get("TEST_LOG_DIR", None) +if log_dir is not None: + MAIN_LOG_PATH = log_dir + "/" + MAIN_LOG_PATH +else: + MAIN_LOG_PATH = "./" + MAIN_LOG_PATH + + +MODULE_NAME = "DHF-Tests" +logger = logging.getLogger(MODULE_NAME) +logger.setLevel(LOG_LEVEL) + +ch = logging.StreamHandler(sys.stdout) +ch.setLevel(LOG_LEVEL) +ch.setFormatter(logging.Formatter(LOG_FORMAT)) + +fh = logging.FileHandler(MAIN_LOG_PATH) +fh.setLevel(LOG_LEVEL) +fh.setFormatter(logging.Formatter(LOG_FORMAT)) + +if not logger.hasHandlers(): + logger.addHandler(ch) + logger.addHandler(fh) + +try: + from beem import Hive +except Exception as ex: + logger.error("beem library is not installed.") + sys.exit(1) + + +# 1. create few proposals - in this scenario proposals have different starting and ending dates +# 2. vote on them to show differences in asset distribution (depending on collected votes) +# 3. wait for proposal payment phase +# 4. verify (using account history and by checking regular account balance) that given accounts have been correctly paid. + + +if __name__ == '__main__': + logger.info("Performing SPS tests") + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("creator", help = "Account to create test accounts with") + parser.add_argument("treasury", help = "Treasury account") + parser.add_argument("wif", help="Private key for creator account") + parser.add_argument("--node-url", dest="node_url", default="http://127.0.0.1:8090", help="Url of working hive node") + parser.add_argument("--run-hived", dest="hived_path", help = "Path to hived executable. Warning: using this option will erase contents of selected hived working directory.") + parser.add_argument("--working-dir", dest="hived_working_dir", default="/tmp/hived-data/", help = "Path to hived working directory") + parser.add_argument("--config-path", dest="hived_config_path", default="../../hive_utils/resources/config.ini.in",help = "Path to source config.ini file") + parser.add_argument("--no-erase-proposal", action='store_false', dest = "no_erase_proposal", help = "Do not erase proposal created with this test") + + + args = parser.parse_args() + + node = None + + if args.hived_path: + logger.info("Running hived via {} in {} with config {}".format(args.hived_path, + args.hived_working_dir, + args.hived_config_path) + ) + + node = hive_utils.hive_node.HiveNodeInScreen( + args.hived_path, + args.hived_working_dir, + args.hived_config_path + ) + + node_url = args.node_url + wif = args.wif + + if len(wif) == 0: + logger.error("Private-key is not set in config.ini") + sys.exit(1) + + logger.info("Using node at: {}".format(node_url)) + logger.info("Using private-key: {}".format(wif)) + + accounts = [ + # place accounts here in the format: {'name' : name, 'private_key' : private-key, 'public_key' : public-key} + {"name" : "tester001", "private_key" : "5KQeu7SdzxT1DiUzv7jaqwkwv1V8Fi7N8NBZtHugWYXqVFH1AFa", "public_key" : "TST8VfiahQsfS1TLcnBfp4NNfdw67uWweYbbUXymbNiDXVDrzUs7J"}, + {"name" : "tester002", "private_key" : "5KgfcV9bgEen3v9mxkoGw6Rhuf2giDRZTHZjzwisjkrpF4FUh3N", "public_key" : "TST5gQPYm5bs9dRPHpqBy6dU32M8FcoKYFdF4YWEChUarc9FdYHzn"}, + {"name" : "tester003", "private_key" : "5Jz3fcrrgKMbL8ncpzTdQmdRVHdxMhi8qScoxSR3TnAFUcdyD5N", "public_key" : "TST57wy5bXyJ4Z337Bo6RbinR6NyTRJxzond5dmGsP4gZ51yN6Zom"}, + {"name" : "tester004", "private_key" : "5KcmobLVMSAVzETrZxfEGG73Zvi5SKTgJuZXtNgU3az2VK3Krye", "public_key" : "TST8dPte853xAuLMDV7PTVmiNMRwP6itMyvSmaht7J5tVczkDLa5K"}, + ] + + if not accounts: + logger.error("Accounts array is empty, please add accounts in a form {\"name\" : name, \"private_key\" : private_key, \"public_key\" : public_key}") + sys.exit(1) + + keys = [wif] + for account in accounts: + keys.append(account["private_key"]) + + if node is not None: + node.run_hive_node(["--enable-stale-production"]) + try: + if node is None or node.is_running(): + node_client = Hive(node = [node_url], no_broadcast = False, + keys = keys + ) + + logger.info("Chain prefix is: {}".format(node_client.prefix)) + + # create accounts + test_utils.create_accounts(node_client, args.creator, accounts) + # tranfer to vesting + test_utils.transfer_to_vesting(node_client, args.creator, accounts, "300.000", + "TESTS" + ) + + logger.info("Wait 30 days for full voting power") + hive_utils.debug_quick_block_skip(node_client, wif, (30 * 24 * 3600 / 3)) + hive_utils.debug_generate_blocks(node_client.rpc.url, wif, 10) + + # transfer assets to accounts + test_utils.transfer_assets_to_accounts(node_client, args.creator, accounts, + "400.000", "TESTS", wif + ) + + test_utils.transfer_assets_to_accounts(node_client, args.creator, accounts, + "400.000", "TBD", wif + ) + + logger.info("Balances for accounts after initial transfer") + test_utils.print_balance(node_client, accounts) + # transfer assets to treasury + test_utils.transfer_assets_to_treasury(node_client, args.creator, args.treasury, + "1000000.000", "TBD", wif + ) + test_utils.print_balance(node_client, [{'name' : args.treasury}]) + + # create post for valid permlinks + test_utils.create_posts(node_client, accounts, wif) + + now = node_client.get_dynamic_global_properties(False).get('time', None) + if now is None: + raise ValueError("Head time is None") + now = test_utils.date_from_iso(now) + + proposal_data = [ + ['tester001', 1 + 0, 5, '24.000 TBD'], # starts one day from now and lasts five days + ['tester002', 1 + 0, 2, '24.000 TBD'], # starts one day from now and lasts two days + ['tester003', 1 + 2, 1, '24.000 TBD'], # starts three days from now and lasts one day + ['tester004', 1 + 4, 1, '24.000 TBD'] # starts five days from now and lasts one day + ] + + proposals = [ + # pace proposals here in the format: {'creator' : creator, 'receiver' : receiver, 'start_date' : start-date, 'end_date' : end_date} + + ] + + for pd in proposal_data: + start_date, end_date = test_utils.get_start_and_end_date(now, pd[1], pd[2]) + proposal = {'creator' : pd[0], 'receiver' : pd[0], 'start_date' : start_date, 'end_date' : end_date, 'daily_pay' : pd[3]} + proposals.append(proposal) + + import datetime + test_start_date = now + datetime.timedelta(days = 1) + test_start_date_iso = test_utils.date_to_iso(test_start_date) + + test_end_date = test_start_date + datetime.timedelta(days = 6, hours = 1) + test_end_date_iso = test_utils.date_to_iso(test_end_date) + + test_utils.create_proposals(node_client, proposals, wif) + + # list proposals with inactive status, it shoud be list of pairs id:total_votes + test_utils.list_proposals(node_client, test_start_date_iso, "inactive") + + # each account is voting on proposal + test_utils.vote_proposals(node_client, accounts, wif) + + # list proposals with inactive status, it shoud be list of pairs id:total_votes + votes = test_utils.list_proposals(node_client, test_start_date_iso, "inactive") + for vote in votes: + #should be 0 for all + assert vote == 0, "All votes should be equal to 0" + + logger.info("Balances for accounts after creating proposals") + balances = test_utils.print_balance(node_client, accounts) + for balance in balances: + #should be 390.000 TBD for all + assert balance == '390000', "All balances should be equal to 390.000 TBD" + test_utils.print_balance(node_client, [{'name' : args.treasury}]) + + # move forward in time to see if proposals are paid + # moving is made in 1h increments at a time, after each + # increment balance is printed + logger.info("Moving to date: {}".format(test_start_date_iso)) + hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, test_start_date_iso, False) + current_date = test_start_date + while current_date < test_end_date: + current_date = current_date + datetime.timedelta(hours = 1) + current_date_iso = test_utils.date_to_iso(current_date) + + logger.info("Moving to date: {}".format(current_date_iso)) + hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, current_date_iso, False) + + logger.info("Balances for accounts at time: {}".format(current_date_iso)) + test_utils.print_balance(node_client, accounts) + test_utils.print_balance(node_client, [{'name' : args.treasury}]) + + votes = test_utils.list_proposals(node_client, test_start_date_iso, "active") + votes = test_utils.list_proposals(node_client, test_start_date_iso, "expired") + votes = test_utils.list_proposals(node_client, test_start_date_iso, "all") + + # move additional hour to ensure that all proposals ended + logger.info("Moving to date: {}".format(test_end_date_iso)) + hive_utils.common.debug_generate_blocks_until(node_client.rpc.url, wif, test_end_date_iso, False) + logger.info("Balances for accounts at time: {}".format(test_end_date_iso)) + balances = test_utils.print_balance(node_client, accounts) + + #'HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD' == 5 days, so in 5th day any proposal is not paid at all, therefore: + # An account 'tester001' got only 486.000 TBD + # An account 'tester004' didn't get any reward + test_balances = [ + '486000', + '438000', + '414000', + '390000', + ] + + for idx in range(0, len(test_balances)): + assert balances[idx] == test_balances[idx], "Balances dont match {} != {}".format(balances[idx], test_balances[idx]) + + test_utils.print_balance(node_client, [{'name' : args.treasury}]) + + if node is not None: + node.stop_hive_node() + sys.exit(0) + sys.exit(1) + except Exception as ex: + logger.error("Exception: {}".format(ex)) + if node is not None: + node.stop_hive_node() + sys.exit(1) \ No newline at end of file diff --git a/tests/functional/python_tests/dhf_tests/run_proposal_tests.py b/tests/functional/python_tests/dhf_tests/run_proposal_tests.py index 9cd2de3348..261c1bb35d 100644 --- a/tests/functional/python_tests/dhf_tests/run_proposal_tests.py +++ b/tests/functional/python_tests/dhf_tests/run_proposal_tests.py @@ -19,7 +19,8 @@ "proposal_payment_test_006.py", "proposal_payment_test_007.py", "proposal_payment_test_008.py", - "proposal_payment_test_009.py" + "proposal_payment_test_009.py", + "proposal_payment_test_with_governance_010.py" ] parser = argparse.ArgumentParser() @@ -75,7 +76,7 @@ print(f'[FAIL][{stop :.2f}s] {test} failed') try: - v = f"""ps -A -o pid,cmd | grep "{args.hived_working_dir}" | grep -v SCREEN | grep -v grep | grep -v python3 | cut -d '/' -f 1 | xargs kill -9 """ + v = f"""ps -A -o pid,cmd | grep "{args.hived_working_dir}" | grep -v SCREEN | grep -v grep | grep -v python3 | cut -d '/' -f 1 | xargs -r kill -9 """ print(v) system(v) v = f"""mv {joinpath(args.hived_working_dir, "*.log")} {joinpath(args.arti, test.replace('.py', '_hived.log'))} 2>/dev/null""" diff --git a/tests/functional/python_tests/dhf_tests/test_utils.py b/tests/functional/python_tests/dhf_tests/test_utils.py index 1b753474b7..894c272b11 100644 --- a/tests/functional/python_tests/dhf_tests/test_utils.py +++ b/tests/functional/python_tests/dhf_tests/test_utils.py @@ -207,8 +207,8 @@ def calculate_propsal_budget( node, treasury, wif = None) -> int: return round(( v * 0.01 ) / 24.0) def calculate_hourly_pay( budget : int, daily_pay : int ) -> int: - # 416 is ratio in sps_processor, which is constant if proposals are paid in constant periods (in this case 1 hour = 3600 s) - theory = int((daily_pay * 416)/10000) + # 416.6666667 is ratio in sps_processor, which is constant if proposals are paid in constant periods (in this case 1 hour = 3600 s) + theory = int((daily_pay * 416.666666667)/10000) return min(theory, budget) # proposals format: { "acc_name": daily_pay } @@ -219,4 +219,4 @@ def calculate_expected_hourly_payout( proposals : dict, budget : int ) -> dict: payout = calculate_hourly_pay( budget, dpay ) budget -= payout ret[acc] = payout - return ret \ No newline at end of file + return ret diff --git a/tests/functional/python_tests/fork_tests/scan_node_for_duplicates.py b/tests/functional/python_tests/fork_tests/scan_node_for_duplicates.py new file mode 100644 index 0000000000..c9084c1fb7 --- /dev/null +++ b/tests/functional/python_tests/fork_tests/scan_node_for_duplicates.py @@ -0,0 +1,90 @@ +import argparse +import requests +import json +import time + +# we stay delayed several blocks from last_irreversible_block (else this issue would not reproduce) +DELAY = 30 + +def get_lib(url): + properties_query_data = { + "jsonrpc": "2.0", + "method": "call", + "params": [ + "database_api", + "get_dynamic_global_properties", + {} + ], + "id": 1 + } + + json_data = bytes(json.dumps(properties_query_data), "utf-8") + b"\r\n" + result = requests.post(url, data=json_data) + response = json.loads(result.text) + last_irreversible_block = response["result"]["last_irreversible_block_num"] + return last_irreversible_block + + +def get_ops(url, block_num): + ops_query_data = { + "jsonrpc": "2.0", + "method": "call", + "params": [ + "account_history_api", + "get_ops_in_block", + { + "block_num": block_num, + "only_virtual": False + } + ], + "id": 1 + } + json_data = bytes(json.dumps(ops_query_data), "utf-8") + b"\r\n" + result = requests.post(url, data=json_data) + response = json.loads(result.text) + ops = response["result"]["ops"] + return ops + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('url', metavar='https://api.hive.blog', nargs=1, type=str, help="endpoint protocol://ip:port") + + args = parser.parse_args() + + url = args.url[0] + print('url: ', url) + + start_lib = get_lib(url) + scanned_until = start_lib + last_irreversible_block = start_lib + + while True: + while True: + last_irreversible_block = get_lib(url) + #print("last_irreversible_block: " + str(last_irreversible_block)) + + if last_irreversible_block == scanned_until: + time.sleep(1) + else: + break + print(f"last_irreversible_block: {last_irreversible_block}") + + for block_num in range(scanned_until-DELAY, last_irreversible_block-DELAY): + ops = get_ops(url, block_num) + + duplicates_count = 0 + for j in range(len(ops)): + for i in range(j): + op1 = ops[i] + op2 = ops[j] + if op1==op2: + duplicates_count += 1 + print(f"FOUND DUPLICATES IN BLOCK {block}") + print(op1) + print(op2) + + if duplicates_count == 0: + print(f"NO DUPLICATES IN BLOCK {block_num}") + + scanned_until = last_irreversible_block + diff --git a/tests/functional/python_tests/hived/replay_based_tests/README.md b/tests/functional/python_tests/hived/replay_based_tests/README.md new file mode 100644 index 0000000000..135ae6ab48 --- /dev/null +++ b/tests/functional/python_tests/hived/replay_based_tests/README.md @@ -0,0 +1,25 @@ +# Replay Based Tests + +#### Note + +##### All python files in this directory, will be started after 3.5 million block replay will finish + +--- + +## Flag requirements + +- ### `--run-hived ` + +address and port to currently running node + +#### Example + + python3 sample_test.py --run-hived 127.0.0.1:8090 + +- ### `--path-to-config ` + +path to `config.ini` file of currently running node + +#### Example + + python3 sample_test.py --path-to-config /home/user/datadir/config.ini diff --git a/tests/functional/python_tests/hived/replay_based_tests/vop_pagnation.py b/tests/functional/python_tests/hived/replay_based_tests/vop_pagnation.py new file mode 100755 index 0000000000..df6b6d81d7 --- /dev/null +++ b/tests/functional/python_tests/hived/replay_based_tests/vop_pagnation.py @@ -0,0 +1,174 @@ +#!/usr/bin/python3 + +import sys +import os +import tempfile +import argparse +from threading import Thread + +sys.path.append("../../../") + +import hive_utils +from hive_utils.resources.configini import config as configuration +from hive_utils.resources.configini import validate_address + + +# https://developers.hive.io/tutorials-recipes/paginated-api-methods.html#account_history_apiget_account_history +MAX_AT_ONCE = 10000 + +parser = argparse.ArgumentParser() +parser.add_argument("--run-hived", dest="hived", help = "IP address to replayed node", required=True, type=str) +parser.add_argument("--path-to-config", dest="config_path", help = "Path to node config file", required=True, type=str, default=None) +parser.add_argument("--blocks", dest="blocks", help = "Blocks to replay", required=False, type=int, default=1000000) + +args = parser.parse_args() +node = None + +assert int(args.blocks) >= 1000000, "replay has to be done for more than 1 million blocks" + +# config +config = configuration() +config.load(args.config_path) + +# check existance of required plugins +plugins = config.plugin.split(' ') +assert "account_history_rocksdb" in plugins +assert "account_history_api" in plugins + +# class that compressing vop +class compressed_vop: + def __init__(self, vop): + from hashlib import sha512 + from random import randint + from json import dumps + + self.id = "{}_{}_{}".format( (~0x8000000000000000) & int(vop["operation_id"]), vop["block"], vop["trx_in_block"]) + self.checksum = sha512( dumps(vop).encode() ).hexdigest() + # self.content = vop + + def get(self): + return self.__dict__ + +# return compressed data from api call +def compress_vops(data : list) -> list: + ret = [] + for vop in data: + ret.append(compressed_vop(vop).get()) + return ret + +# this function do call to API +def get_vops(range_begin : int, range_end : int, start_from_id : int, limit : int) -> dict: + global config + + from requests import post + from json import dumps + + # from time import sleep + # sleep(0.25) + + data = { + "jsonrpc":"2.0", + "method":"call", + "params":[ + "account_history_api", + "enum_virtual_ops", + { + "block_range_begin":range_begin, + "block_range_end":range_end, + "operation_begin":start_from_id, + "limit":limit + } + ], + "id":1 + } + + ret = post(f"http://{config.webserver_http_endpoint}", data=dumps(data)) + if ret.status_code == 200: + return ret.json()['result'] + else: + raise Exception("bad request") + +# checks is there anythink more to get +def paginated(data : dict, range_end : int) -> bool: + return not ( data['next_operation_begin'] == 0 and ( data['next_block_range_begin'] == range_end or data['next_block_range_begin'] == 0 ) ) + +# do one huge call +def get_vops_at_once(range_begin : int, range_end : int) -> list: + tmp = get_vops(range_begin, range_end, 0, MAX_AT_ONCE) + assert not paginated(tmp, range_end) + return compress_vops(tmp['ops']) + +# generator, that get page by page in step given as limit +def get_vops_paginated(range_begin : int, range_end : int, limit : int): + ret = get_vops(range_begin, range_end, 0, limit) + yield compress_vops(ret['ops']) + if not paginated(ret, range_end): + ret = None + while ret is not None: + ret = get_vops(ret['next_block_range_begin'], range_end, ret['next_operation_begin'], limit) + yield compress_vops(ret['ops']) + if not paginated(ret, range_end): + ret = None + yield None + +# wrapper on generator that agregates paginated output +def get_vops_with_step(range_begin : int, range_end : int, limit : int) -> list: + next_object = get_vops_paginated(range_begin, range_end, limit) + ret = [] + value = next(next_object) + while value is not None: + ret.extend(value) + value = next(next_object) + return ret + +# proxy, to get_vops_with_step with limit set as 1 +def get_vops_one_by_one(range_begin : int, range_end : int) -> list: + return get_vops_with_step(range_begin, range_end, 1) + +# get same data in given range with diffrent step +def check_range(range_begin : int, blocks : int): + from operator import itemgetter + from json import dump + + range_end = range_begin + blocks + 1 + + print(f"gathering blocks in range [ {range_begin} ; {range_end} )") + + all_at_once = get_vops_at_once(range_begin, range_end) + paginated_by_1 = get_vops_one_by_one(range_begin, range_end) + paginated_by_2 = get_vops_with_step(range_begin, range_end, 2) + paginated_by_5 = get_vops_with_step(range_begin, range_end, 5) + paginated_by_10 = get_vops_with_step(range_begin, range_end, 10) + + # dump(all_at_once, open("all_at_once.json", 'w')) + # dump(paginated_by_1, open("paginated_by_1.json", 'w')) + # dump(paginated_by_2, open("paginated_by_2.json", 'w')) + # dump(paginated_by_5, open("paginated_by_5.json", 'w')) + # dump(paginated_by_10, open("paginated_by_10.json", 'w')) + + assert all_at_once == paginated_by_1 + print(f"[OK] all == paginated by 1 [ {range_begin} ; {range_end} )") + assert all_at_once == paginated_by_2 + print(f"[OK] all == paginated by 2 [ {range_begin} ; {range_end} )") + assert all_at_once == paginated_by_5 + print(f"[OK] all == paginated by 5 [ {range_begin} ; {range_end} )") + assert all_at_once == paginated_by_10 + print(f"[OK] all == paginated by 10 [ {range_begin} ; {range_end} )") + + return True + +threads = [] +STEP = 100 + +# start tests in diffrent threads +for i in range(args.blocks - (STEP * 4), args.blocks, STEP): + th = Thread(target=check_range, args=(i, STEP)) + th.start() + threads.append( th ) + +for job in threads: + job.join() + +print("success") + +exit(0) diff --git a/tests/functional/python_tests/hived/snapshot_1.py b/tests/functional/python_tests/hived/snapshot_1.py new file mode 100755 index 0000000000..e9d1c22023 --- /dev/null +++ b/tests/functional/python_tests/hived/snapshot_1.py @@ -0,0 +1,142 @@ +#!/usr/bin/python3 + +import sys +import os +import tempfile +import argparse + +sys.path.append("../../") + +import hive_utils +from hive_utils.resources.configini import config as configuration +from subprocess import PIPE, STDOUT + + +parser = argparse.ArgumentParser() +parser.add_argument("--run-hived", dest="hived", help = "Path to hived executable", required=True, type=str) +parser.add_argument("--block-log", dest="block_log_path", help = "Path to block log", required=True, type=str, default=None) +parser.add_argument("--blocks", dest="blocks", help = "Blocks to replay", required=False, type=int, default=1000) +parser.add_argument("--leave", dest="leave", action='store_true') +parser.add_argument("--artifact-directory", dest="artifacts", help = "Path to directory where logs will be stored", required=False, type=str) + +args = parser.parse_args() +node = None + +assert args.hived + +# working dir +from uuid import uuid5, NAMESPACE_URL +from random import randint +work_dir = os.path.join( tempfile.gettempdir(), uuid5(NAMESPACE_URL,str(randint(0, 1000000))).__str__().replace("-", "")) +os.mkdir(work_dir) + + +# config paths +config_file_name = 'config.ini' +path_to_config = os.path.join(work_dir, config_file_name) + +# snapshot dir +snapshot_root = os.path.join(work_dir, "snapshots") +os.mkdir(snapshot_root) + +# setting up block log +blockchain_dir = os.path.join(work_dir, "blockchain") +os.mkdir( blockchain_dir ) +assert args.block_log_path +os.symlink(args.block_log_path, os.path.join(blockchain_dir, "block_log")) + +# config +config = configuration() +config.witness = None # no witness +config.private_key = None # also no prv key +config.snapshot_root_dir = snapshot_root +config.plugin = config.plugin + " state_snapshot" # this plugin is required + +# config generation +config.generate(path_to_config) + +base_hv_args = [ "--stop-replay-at-block", str(args.blocks), "--exit-after-replay" ] +hv_args = base_hv_args.copy() +hv_args.append("--replay-blockchain") + +# setting up logging +stdout = PIPE +stderr = None + +if args.artifacts: + stderr = STDOUT + stdout = open(os.path.join(args.artifacts, "replayed_node_snapshot_1.log"), 'w', 1) + +# setup for replay +node = hive_utils.hive_node.HiveNode( + args.hived, + work_dir, + hv_args.copy(), + stdout, + stderr +) + +# replay +print(f"waiting for replay of {args.blocks} blocks...") +with node: + node.wait_till_end() +print("replay completed, creating snapshot") + +# setup for first snapshot +first_snapshot_name = "first_snapshot" +hv_args = base_hv_args.copy() +hv_args.extend(["--dump-snapshot", first_snapshot_name]) +node.hived_args = hv_args.copy() + +# creating snapshot +print("creating snapshot '{}' ...".format(first_snapshot_name)) +with node: + node.wait_till_end() + +# setup for loading snapshot +hv_args = base_hv_args.copy() +hv_args.extend(["--load-snapshot", first_snapshot_name]) +node.hived_args = hv_args.copy() +os.remove(os.path.join(blockchain_dir, "shared_memory.bin")) + +# loading snapshot +print("creating snapshot '{}' ...".format(first_snapshot_name)) +with node: + node.wait_till_end() + +# setup for redumping snapshot +second_snapshot_name = "second_snapshot" +hv_args = base_hv_args.copy() +hv_args.extend(["--dump-snapshot", second_snapshot_name]) +node.hived_args = hv_args.copy() + +# redumpping snapshot +print("creating snapshot '{}' ...".format(second_snapshot_name)) +with node: + node.wait_till_end() + +path_to_first_snapshot = os.path.join(snapshot_root, first_snapshot_name) +path_to_second_snapshot = os.path.join(snapshot_root, second_snapshot_name) + + +from hive_utils.common import compare_snapshots +miss_list = compare_snapshots(path_to_first_snapshot, path_to_second_snapshot) + +if len(miss_list) is not 0: + for val in miss_list: + print("checksum missmatch: '{}'".format(val)) +else: + print("all checksums matches") + + +if not args.leave: + from shutil import rmtree + rmtree( work_dir ) + print("deleted: {}".format(work_dir)) +else: + print("datadir not deleted: {}".format(work_dir)) + +if stderr is not None: + stdout.close() + +exit(len(miss_list)) diff --git a/tests/functional/python_tests/hived/snapshot_2.py b/tests/functional/python_tests/hived/snapshot_2.py new file mode 100755 index 0000000000..6ab2e2caf4 --- /dev/null +++ b/tests/functional/python_tests/hived/snapshot_2.py @@ -0,0 +1,192 @@ +#!/usr/bin/python3 + +import sys +import os +import tempfile +import argparse +from subprocess import PIPE, STDOUT + +sys.path.append("../../") + +import hive_utils +from hive_utils.resources.configini import config as configuration +from hive_utils.common import wait_n_blocks + + +parser = argparse.ArgumentParser() +parser.add_argument("--run-hived", dest="hived", help = "Path to hived executable", required=True, type=str) +parser.add_argument("--block-log", dest="block_log_path", help = "Path to block log", required=True, type=str, default=None) +parser.add_argument("--blocks", dest="blocks", help = "Blocks to replay", required=False, type=int, default=1000) +parser.add_argument("--leave", dest="leave", action='store_true') +parser.add_argument("--artifact-directory", dest="artifacts", help = "Path to directory where logs will be stored", required=False, type=str) + +args = parser.parse_args() +node = None + +assert args.hived + +# working dir +from uuid import uuid5, NAMESPACE_URL +from random import randint +work_dir = os.path.join( tempfile.gettempdir(), uuid5(NAMESPACE_URL,str(randint(0, 1000000))).__str__().replace("-", "")) +os.mkdir(work_dir) + + +# config paths +config_file_name = 'config.ini' +path_to_config = os.path.join(work_dir, config_file_name) + +# snapshot dir +snapshot_root = os.path.join(work_dir, "snapshots") +# os.mkdir(snapshot_root) + +# setting up block log +blockchain_dir = os.path.join(work_dir, "blockchain") +os.mkdir( blockchain_dir ) +assert args.block_log_path +os.symlink(args.block_log_path, os.path.join(blockchain_dir, "block_log")) + +# config +config = configuration() +config.witness = None # no witness +config.private_key = None # also no prv key +config.snapshot_root_dir = snapshot_root +config.plugin = config.plugin + " state_snapshot" # this plugin is required + +# config generation +config.generate(path_to_config) + +def get_base_hv_args(): + return [ "--stop-replay-at-block", str(args.blocks), "--exit-after-replay" ].copy() + +def dump_snapshot(Node, snapshot_name): +# setup for snapshot + hv_args = get_base_hv_args() + hv_args.extend(["--dump-snapshot", snapshot_name]) + Node.hived_args = hv_args +# creating snapshot + print("creating snapshot '{}' ...".format(snapshot_name)) + with Node: + Node.wait_till_end() + +def load_snapshot(Node, snapshot_name): +# setup for loading snapshot + hv_args = get_base_hv_args() + hv_args.extend(["--load-snapshot", snapshot_name]) + Node.hived_args = hv_args + os.remove(os.path.join(blockchain_dir, "shared_memory.bin")) +# loading snapshot + print( "loading snapshot '{}' ...".format(snapshot_name)) + with Node: + Node.wait_till_end() + +def run_for_n_blocks(Node, blocks : int, additional_args : list = []): + args.blocks += blocks + Node.hived_args = get_base_hv_args() + if len(additional_args) > 0: + Node.hived_args.extend(additional_args) + print("waiting for {} blocks...".format(int(args.blocks))) + with Node: + Node.wait_till_end() + +def require_success(node): + assert node.last_returncode == 0 + +def require_fail(node, filepath): + with open(filepath, 'r') as fff: + for line in fff: + if line.find("Snapshot generation FAILED."): + return + assert False + +hv_args = get_base_hv_args() +hv_args.append("--replay-blockchain") + +# setting up logging +stdout = PIPE +stderr = None + +if args.artifacts: + stderr = STDOUT + stdout = open(os.path.join(args.artifacts, "replayed_node_snapshot_2.log"), 'w', 1) + +# setup for replay +node = hive_utils.hive_node.HiveNode( + args.hived, + work_dir, + hv_args, + stdout, + stderr +) + +# replay +print("waiting for replay of {} blocks...".format(int(args.blocks))) +with node: + node.wait_till_end() +require_success(node) +print("replay completed, creating snapshot") + + +# try to create snapshot, while directory not exist +assert not os.path.exists(snapshot_root) +dump_snapshot(node, "snap_1") +require_success(node) +assert os.path.exists(snapshot_root) +assert os.path.exists(os.path.join(snapshot_root, "snap_1")) + +# load snapshot +load_snapshot(node, "snap_1") +require_success(node) + +# check is replaying +run_for_n_blocks(node, 100, ["--replay-blockchain"]) +require_success(node) + +# dump to same directory +# change output stream +tmp_filel = "tmp_file.log" +tmp_output = open(tmp_filel, 'w') +current_outuput = node.stdout_stream +node.stdout_stream = tmp_output + +# gather output +dump_snapshot(node, "snap_1") + +# optionally append output to artiffacts +tmp_output.close() +node.stdout_stream = current_outuput +if stderr is not None: + with open(tmp_filel, 'r'): + for line in tmp_filel: + stdout.write(line) + +# check is failed +require_fail(node, tmp_filel) +os.remove(tmp_filel) + +from shutil import rmtree +rmtree(os.path.join(config.snapshot_root_dir, "snap_1")) +dump_snapshot(node, "snap_1") +require_success(node) + +# load snapshot +load_snapshot(node, "snap_1") +require_success(node) + +# check is replaying +run_for_n_blocks(node, 100, ["--replay-blockchain"]) +require_success(node) + +print("success") + +if not args.leave: + from shutil import rmtree + rmtree( work_dir ) + print("deleted: {}".format(work_dir)) +else: + print("datadir not deleted: {}".format(work_dir)) + +if stderr is not None: + stdout.close() + +exit(0) \ No newline at end of file diff --git a/tests/functional/python_tests/hived/start_replay_tests.py b/tests/functional/python_tests/hived/start_replay_tests.py new file mode 100755 index 0000000000..bf068bc6d9 --- /dev/null +++ b/tests/functional/python_tests/hived/start_replay_tests.py @@ -0,0 +1,169 @@ +#!/usr/bin/python3 + +import sys +import os +import tempfile +import argparse +import subprocess as sub + +sys.path.append("../../") + +import hive_utils +from hive_utils.hive_node import HiveNode +from hive_utils.resources.configini import config as configuration + +parser = argparse.ArgumentParser() +parser.add_argument("--run-hived", dest="hived", help = "Path to hived executable", required=True, type=str) +parser.add_argument("--block-log", dest="block_log_path", help = "Path to block log", required=True, type=str, default=None) +parser.add_argument("--test-directory", dest="test_dir", help = "Path to directory with tests", required=True, type=str) +parser.add_argument("--blocks", dest="blocks", help = "Blocks to replay", required=False, type=int, default=5000000) +parser.add_argument("--artifact-directory", dest="artifacts", help = "Path to directory where logs will be stored", required=False, type=str) +parser.add_argument("--leave", dest="leave", action='store_true') + +args = parser.parse_args() + +# check is blocks to replay are in range +assert args.blocks <= 5000000, "cannot replay more than 5 million blocks" +if args.blocks < 1000000: + from warnings import warn + warn("replay under 1 million blocks can be unreliable") + +# config setup +config = configuration() +config.witness = None +config.private_key = None +config.webserver_http_endpoint = "127.0.0.1:8100" +config.exclude_plugins(["witness"]) +config.update_plugins([ + + "account_by_key", "account_by_key_api", "condenser_api", + "account_history_rocksdb", "account_history_api", "block_api", + "chain_api" + +]) + +# creating working dir +from uuid import uuid5, NAMESPACE_URL +from random import randint +work_dir = os.path.join( tempfile.gettempdir(), uuid5(NAMESPACE_URL,str(randint(0, 1000000))).__str__().replace("-", "")) +os.mkdir(work_dir) + +# setting up block log +assert args.block_log_path +blockchain_dir = os.path.join(work_dir, "blockchain") + +os.mkdir( blockchain_dir ) +os.symlink(args.block_log_path, os.path.join(blockchain_dir, "block_log")) + +# generating config +path_to_config = os.path.join(work_dir, "config.ini") +config.generate(path_to_config) + +# setting up logging +stdout = sub.PIPE +stderr = None + +if args.artifacts: + stderr = sub.STDOUT + stdout = open(os.path.join(args.artifacts, "replayed_node.log"), 'w', 1) + +# creating node +node = HiveNode( + args.hived, + work_dir, + ["--stop-replay-at-block", str(args.blocks), "--replay-blockchain", "--exit-after-replay"], + stdout, + stderr +) + +# replay +with node: + node.wait_till_end() + +node.hived_args = ["--stop-replay-at-block", str(args.blocks), "--replay-blockchain"] + +# check success +assert node.last_returncode == 0 + +# gather aviable tests +os.chdir(args.test_dir) +from os import walk +(_, _, filenames) = next(walk(args.test_dir)) +tests = [x for x in filenames if x.endswith('.py')] + +# tests +PID : int = None +RETCODE : int = 0 +MAX_TIME_FOR_EACH_TEST_IN_SECONDS = 15 * 60 + +if args.artifacts: + if not os.path.exists(args.artifacts): + os.mkdir(args.artifacts) + +try: + with node: + + # gather required info[] + PID = node.hived_process.pid + + # start tests + for test in tests: + + output = sub.DEVNULL + + if args.artifacts: + log_filename = test.split('.')[0] + ".log" + path_to_save = os.path.join(args.artifacts, log_filename) + output = open(path_to_save, 'w', 1) + + T = sub.Popen(["/usr/bin/python3", test, "--run-hived", config.webserver_http_endpoint, "--path-to-config", path_to_config], stdout=output, stderr=sub.STDOUT) + retcode = T.wait(MAX_TIME_FOR_EACH_TEST_IN_SECONDS) + if retcode != 0: + RETCODE -= 1 + print(f"[FAILED] {test}") + else: + print(f"[OK] {test}") + + if output is not sub.DEVNULL: + # output.flush() + output.close() + + +except Exception as exception: + + RETCODE -= 100 + print(f"Exception occured:\n####\n\n{exception}\n\n####") + +finally: + + from psutil import pid_exists, Process + while pid_exists(PID): + proc = Process(PID) + timeout = 5 + try: + print(f"[{timeout} seconds] Trying to kill process PID: {PID}") + proc.kill() + proc.wait(timeout) + except: + pass + finally: + if pid_exists(PID): + try: + print(f"[{timeout} seconds] Trying to kill process PID: {PID}") + proc.terminate() + proc.wait(timeout) + except: + print(f"Process is out of controll PID: {PID}") + + + if stderr is not None: + stdout.close() + + if not args.leave: + from shutil import rmtree + rmtree( work_dir ) + print("deleted: {}".format(work_dir)) + else: + print("datadir not deleted: {}".format(work_dir)) + + exit(RETCODE) diff --git a/tests/functional/python_tests/last_irreversible_block_tests/test_irreversible_block_while_sync.py b/tests/functional/python_tests/last_irreversible_block_tests/test_irreversible_block_while_sync.py new file mode 100644 index 0000000000..738d76bff5 --- /dev/null +++ b/tests/functional/python_tests/last_irreversible_block_tests/test_irreversible_block_while_sync.py @@ -0,0 +1,27 @@ +from test_tools import logger, World +import pytest + + +@pytest.mark.timeout(90) +def test_irreversible_block_while_sync(): + with World() as world: + net = world.create_network('Net') + init_node = net.create_init_node() + api_node = net.create_node() + + # Prepare config + for node in net.nodes: + node.config.shared_file_size = '54M' + + logger.info('Running network, waiting for live sync...') + net.run() + + logger.info(f'Waiting for block number {23}...') + init_node.wait_for_block_with_number(23) + + # Test api_node will enter live sync after restart + logger.info("Restarting api node...") + api_node.close() + api_node.run(use_existing_config=False) + # TODO use node.wait_number_of_blocks(1, timeout=20) when test_tools submodule is updated + api_node.wait_number_of_blocks(1) \ No newline at end of file diff --git a/tests/test_tools b/tests/test_tools new file mode 160000 index 0000000000..360dea6834 --- /dev/null +++ b/tests/test_tools @@ -0,0 +1 @@ +Subproject commit 360dea683439edc6ac78e502d424490f2dc14d93 diff --git a/tests/unit/BoostTestTargets.cmake b/tests/unit/BoostTestTargets.cmake index d0edf7ed25..ae3f78b9e2 100644 --- a/tests/unit/BoostTestTargets.cmake +++ b/tests/unit/BoostTestTargets.cmake @@ -46,11 +46,11 @@ set(BOOST_TEST_TARGET_PREFIX "boosttest") if(NOT Boost_FOUND) find_package(Boost 1.34.0 QUIET) endif() -if("${Boost_VERSION}0" LESS "1034000") +if("${Boost_VERSION_MACRO}0" LESS "1034000") set(_shared_msg "NOTE: boost::test-based targets and tests cannot " "be added: boost >= 1.34.0 required but not found. " - "(found: '${Boost_VERSION}'; want >=103400) ") + "(found: '${Boost_VERSION_MACRO}'; want >=103400) ") if(BUILD_TESTING) message(FATAL_ERROR ${_shared_msg} @@ -66,7 +66,7 @@ endif() include(GetForceIncludeDefinitions.cmake) include(CopyResourcesToBuildTree.cmake) -if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000") +if(Boost_FOUND AND NOT "${Boost_VERSION_MACRO}0" LESS "1034000") set(_boosttesttargets_libs) set(_boostConfig "BoostTestTargetsIncluded.h") if(NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY) @@ -131,7 +131,7 @@ function(add_boost_test _name) "Syntax error in use of add_boost_test: at least one source file required!") endif() - if(Boost_FOUND AND NOT "${Boost_VERSION}0" LESS "1034000") + if(Boost_FOUND AND NOT "${Boost_VERSION_MACRO}0" LESS "1034000") include_directories(${Boost_INCLUDE_DIRS}) @@ -223,7 +223,7 @@ function(add_boost_test _name) set(_test_command ${_target_name}) endif() - if(TESTS AND ( "${Boost_VERSION}" VERSION_GREATER "103799" )) + if(TESTS AND ( "${Boost_VERSION_MACRO}" VERSION_GREATER "103799" )) foreach(_test ${TESTS}) add_test( unit/${_name}-${_test} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index bf91803cba..0083de25b0 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -171,6 +171,11 @@ add_boost_test( chain_test operation_tests/account_update2_validate operation_tests/account_update2_authorities operation_tests/account_update2_apply + operation_tests/recurrent_transfer_max_transfer_processed_per_block + operation_tests/recurrent_transfer_max_open_transfers + operation_tests/recurrent_transfer_hbd + operation_tests/recurrent_transfer_apply + operation_tests/recurrent_transfer_validate operation_time_tests/comment_payout_equalize operation_time_tests/comment_payout_dust operation_time_tests/recent_claims_decay @@ -188,10 +193,14 @@ add_boost_test( chain_test operation_time_tests/clear_null_account operation_time_tests/generate_account_subsidies operation_time_tests/account_subsidy_witness_limits + operation_time_tests/recurrent_transfer_consecutive_failure_deletion + operation_time_tests/recurrent_transfer_expiration proposal_tests/generating_payments proposal_tests/generating_payments_01 proposal_tests/generating_payments_02 proposal_tests/generating_payments_03 + proposal_tests/generating_payments_04 + proposal_tests/generating_payments_05 proposal_tests/proposals_maintenance proposal_tests/proposal_object_apply proposal_tests/proposal_vote_object_apply diff --git a/tests/unit/db_fixture/database_fixture.cpp b/tests/unit/db_fixture/database_fixture.cpp index f1c2b5b219..4d103d48e2 100644 --- a/tests/unit/db_fixture/database_fixture.cpp +++ b/tests/unit/db_fixture/database_fixture.cpp @@ -46,7 +46,7 @@ typedef hive::plugins::account_history::account_history_plugin ah_plugin; using std::cout; using std::cerr; -clean_database_fixture::clean_database_fixture( uint16_t shared_file_size_in_mb ) +clean_database_fixture::clean_database_fixture( uint16_t shared_file_size_in_mb, fc::optional hardfork ) { try { int argc = boost::unit_test::framework::master_test_suite().argc; @@ -87,9 +87,7 @@ clean_database_fixture::clean_database_fixture( uint16_t shared_file_size_in_mb open_database( shared_file_size_in_mb ); - generate_block(); - db->set_hardfork( HIVE_BLOCKCHAIN_VERSION.minor_v() ); - generate_block(); + inject_hardfork( hardfork.valid() ? ( *hardfork ) : HIVE_BLOCKCHAIN_VERSION.minor_v() ); vest( "initminer", 10000 ); @@ -133,7 +131,7 @@ void clean_database_fixture::validate_database() appbase::app().get_plugin< hive::plugins::rc::rc_plugin >().validate_database(); } -void clean_database_fixture::resize_shared_mem( uint64_t size ) +void clean_database_fixture::resize_shared_mem( uint64_t size, fc::optional hardfork ) { db->wipe( data_dir->path(), data_dir->path(), true ); int argc = boost::unit_test::framework::master_test_suite().argc; @@ -161,10 +159,7 @@ void clean_database_fixture::resize_shared_mem( uint64_t size ) boost::program_options::variables_map options; - - generate_block(); - db->set_hardfork( HIVE_BLOCKCHAIN_VERSION.minor_v() ); - generate_block(); + inject_hardfork( hardfork.valid() ? ( *hardfork ) : HIVE_BLOCKCHAIN_VERSION.minor_v() ); vest( "initminer", 10000 ); @@ -179,6 +174,43 @@ void clean_database_fixture::resize_shared_mem( uint64_t size ) validate_database(); } +void clean_database_fixture::inject_hardfork( uint32_t hardfork ) +{ + generate_block(); + db->set_hardfork( hardfork ); + generate_block(); +} + +hardfork_database_fixture::hardfork_database_fixture( uint16_t shared_file_size_in_mb, uint32_t hardfork ) + : clean_database_fixture( shared_file_size_in_mb, hardfork ) +{ +} + +hardfork_database_fixture::~hardfork_database_fixture() +{ +} + +cluster_database_fixture::cluster_database_fixture( uint16_t _shared_file_size_in_mb ) + : shared_file_size_in_mb( _shared_file_size_in_mb ) +{ +} + +cluster_database_fixture::~cluster_database_fixture() +{ +} + +void cluster_database_fixture::execute_24( content_method content ) +{ + ptr_hardfork_database_fixture executor( new hardfork_database_fixture( shared_file_size_in_mb, 24/*hardfork*/ ) ); + content( executor ); +} + +void cluster_database_fixture::execute_25( content_method content ) +{ + ptr_hardfork_database_fixture executor( new hardfork_database_fixture( shared_file_size_in_mb, 25/*hardfork*/ ) ); + content( executor ); +} + live_database_fixture::live_database_fixture() { try @@ -268,7 +300,6 @@ void database_fixture::open_database( uint16_t shared_file_size_in_mb ) args.hbd_initial_supply = HBD_INITIAL_TEST_SUPPLY; args.shared_file_size = 1024 * 1024 * shared_file_size_in_mb; // 8MB(default) or more: file for testing args.database_cfg = hive::utilities::default_database_configuration(); - args.sps_remove_threshold = 20; db->open(args); } else @@ -295,13 +326,18 @@ void database_fixture::generate_blocks(fc::time_point_sec timestamp, bool miss_i BOOST_REQUIRE( ( db->head_block_time() - timestamp ).to_seconds() < HIVE_BLOCK_INTERVAL ); } -void database_fixture::generate_days_blocks( uint32_t days, bool skip_interm_blocks ) +void database_fixture::generate_seconds_blocks( uint32_t seconds, bool skip_interm_blocks ) { fc::time_point_sec timestamp = db->head_block_time(); - timestamp += fc::days(days); + timestamp += fc::seconds(seconds); generate_blocks( timestamp, skip_interm_blocks ); } +void database_fixture::generate_days_blocks( uint32_t days, bool skip_interm_blocks ) +{ + generate_seconds_blocks( days * 24 * 3600, skip_interm_blocks ); +} + fc::string database_fixture::get_current_time_iso_string() const { fc::time_point_sec current_time = db->head_block_time(); @@ -422,7 +458,8 @@ void database_fixture::fund( void database_fixture::fund( const string& account_name, - const asset& amount + const asset& amount, + bool update_print_rate ) { try @@ -437,6 +474,19 @@ void database_fixture::fund( return; } + const auto& median_feed = db.get_feed_history(); + if( amount.symbol == HBD_SYMBOL ) + { + if( median_feed.current_median_history.is_null() ) + db.modify( median_feed, [&]( feed_history_object& f ) + { + f.current_median_history = price( asset( 1, HBD_SYMBOL ), asset( 1, HIVE_SYMBOL ) ); + f.market_median_history = f.current_median_history; + f.current_min_history = f.current_median_history; + f.current_max_history = f.current_median_history; + } ); + } + db.modify( db.get_account( account_name ), [&]( account_object& a ) { if( amount.symbol == HIVE_SYMBOL ) @@ -453,20 +503,14 @@ void database_fixture::fund( if( amount.symbol == HIVE_SYMBOL ) gpo.current_supply += amount; else if( amount.symbol == HBD_SYMBOL ) + { gpo.current_hbd_supply += amount; + gpo.virtual_supply = gpo.current_supply + gpo.current_hbd_supply * median_feed.current_median_history; + } }); - if( amount.symbol == HBD_SYMBOL ) - { - const auto& median_feed = db.get_feed_history(); - if( median_feed.current_median_history.is_null() ) - db.modify( median_feed, [&]( feed_history_object& f ) - { - f.current_median_history = price( asset( 1, HBD_SYMBOL ), asset( 1, HIVE_SYMBOL ) ); - }); - } - - db.update_virtual_supply(); + if( update_print_rate ) + db.update_virtual_supply(); }, default_skip ); } FC_CAPTURE_AND_RETHROW( (account_name)(amount) ) @@ -521,6 +565,15 @@ void database_fixture::transfer( } FC_CAPTURE_AND_RETHROW( (from)(to)(amount) ) } +void database_fixture::push_transaction( const operation& op, const fc::ecc::private_key& key ) +{ + signed_transaction tx; + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + tx.operations.push_back( op ); + sign( tx, key ); + db->push_transaction( tx, 0 ); +} + void database_fixture::vest( const string& from, const string& to, const asset& amount ) { try @@ -571,6 +624,18 @@ void database_fixture::vest( const string& from, const share_type& amount ) } FC_CAPTURE_AND_RETHROW( (from)(amount) ) } +void database_fixture::vest( const string& from, const string& to, const asset& amount, const fc::ecc::private_key& key ) +{ + FC_ASSERT( amount.symbol == HIVE_SYMBOL, "Can only vest TESTS" ); + + transfer_to_vesting_operation op; + op.from = from; + op.to = to; + op.amount = amount; + + push_transaction( op, key ); +} + void database_fixture::proxy( const string& account, const string& proxy ) { try @@ -585,7 +650,7 @@ void database_fixture::proxy( const string& account, const string& proxy ) } FC_CAPTURE_AND_RETHROW( (account)(proxy) ) } -void database_fixture::set_price_feed( const price& new_price ) +void database_fixture::set_price_feed( const price& new_price, bool stop_at_update_block ) { for( size_t i = 1; i < 8; i++ ) { @@ -600,7 +665,10 @@ void database_fixture::set_price_feed( const price& new_price ) trx.clear(); } - generate_blocks( HIVE_BLOCKS_PER_HOUR ); + if( stop_at_update_block ) + generate_blocks( HIVE_FEED_INTERVAL_BLOCKS - ( db->head_block_num() % HIVE_FEED_INTERVAL_BLOCKS ) ); + else + generate_blocks( HIVE_BLOCKS_PER_HOUR ); BOOST_REQUIRE( #ifdef IS_TEST_NET @@ -690,6 +758,55 @@ asset database_fixture::get_vest_rewards_as_hive( const string& account_name )co return db->get_account( account_name ).get_vest_rewards_as_hive(); } +void database_fixture::post_comment_internal( const std::string& _author, const std::string& _permlink, const std::string& _title, const std::string& _body, const std::string& _parent_permlink, const fc::ecc::private_key& _key ) +{ + comment_operation comment; + + comment.author = _author; + comment.permlink = _permlink; + comment.title = _title; + comment.body = _body; + comment.parent_permlink = _parent_permlink; + + signed_transaction trx; + trx.operations.push_back( comment ); + trx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + sign( trx, _key ); + db->push_transaction( trx, 0 ); + trx.signatures.clear(); + trx.operations.clear(); +} + +void database_fixture::post_comment_with_block_generation( std::string _author, std::string _permlink, std::string _title, std::string _body, std::string _parent_permlink, const fc::ecc::private_key& _key) +{ + generate_blocks( db->head_block_time() + HIVE_MIN_ROOT_COMMENT_INTERVAL + fc::seconds( HIVE_BLOCK_INTERVAL ), true ); + + post_comment_internal( _author, _permlink, _title, _body, _parent_permlink, _key ); +} + +void database_fixture::post_comment( std::string _author, std::string _permlink, std::string _title, std::string _body, std::string _parent_permlink, const fc::ecc::private_key& _key) +{ + post_comment_internal( _author, _permlink, _title, _body, _parent_permlink, _key ); +} + +void database_fixture::vote( std::string _author, std::string _permlink, std::string _voter, int16_t _weight, const fc::ecc::private_key& _key ) +{ + vote_operation vote; + + vote.author = _author; + vote.permlink = _permlink; + vote.voter = _voter; + vote.weight = _weight; + + signed_transaction trx; + trx.operations.push_back( vote ); + trx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + sign( trx, _key ); + db->push_transaction( trx, 0 ); + trx.signatures.clear(); + trx.operations.clear(); +} + void database_fixture::sign(signed_transaction& trx, const fc::ecc::private_key& key) { trx.sign( key, db->get_chain_id(), default_sig_canon ); @@ -958,7 +1075,7 @@ void sps_proposal_database_fixture::plugin_prepare() int64_t sps_proposal_database_fixture::create_proposal( std::string creator, std::string receiver, time_point_sec start_date, time_point_sec end_date, - asset daily_pay, const fc::ecc::private_key& key ) + asset daily_pay, const fc::ecc::private_key& key, bool with_block_generation ) { signed_transaction tx; create_proposal_operation op; @@ -975,7 +1092,10 @@ int64_t sps_proposal_database_fixture::create_proposal( std::string creator, std op.subject = std::to_string( cnt ); const std::string permlink = "permlink" + std::to_string( cnt ); - post_comment(creator, permlink, "title", "body", "test", key); + if( with_block_generation ) + post_comment_with_block_generation(creator, permlink, "title", "body", "test", key); + else + post_comment(creator, permlink, "title", "body", "test", key); op.permlink = permlink; @@ -998,7 +1118,7 @@ int64_t sps_proposal_database_fixture::create_proposal( std::string creator, std return itr->proposal_id; } -void sps_proposal_database_fixture::update_proposal(uint64_t proposal_id, std::string creator, asset daily_pay, std::string subject, std::string permlink, const fc::ecc::private_key& key) +void sps_proposal_database_fixture::update_proposal(uint64_t proposal_id, std::string creator, asset daily_pay, std::string subject, std::string permlink, const fc::ecc::private_key& key, time_point_sec* end_date) { signed_transaction tx; update_proposal_operation op; @@ -1009,6 +1129,12 @@ void sps_proposal_database_fixture::update_proposal(uint64_t proposal_id, std::s op.subject = subject; op.permlink = permlink; + if (end_date != nullptr) { + update_proposal_end_date ped; + ped.end_date = *end_date; + op.extensions.insert(ped); + } + tx.operations.push_back( op ); tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); sign( tx, key ); @@ -1095,45 +1221,31 @@ uint64_t sps_proposal_database_fixture::get_nr_blocks_until_daily_maintenance_bl return ret; } -void sps_proposal_database_fixture::post_comment( std::string _authro, std::string _permlink, std::string _title, std::string _body, std::string _parent_permlink, const fc::ecc::private_key& _key) -{ - generate_blocks( db->head_block_time() + HIVE_MIN_ROOT_COMMENT_INTERVAL + fc::seconds( HIVE_BLOCK_INTERVAL ), true ); - comment_operation comment; - - comment.author = _authro; - comment.permlink = _permlink; - comment.title = _title; - comment.body = _body; - comment.parent_permlink = _parent_permlink; - - signed_transaction trx; - trx.operations.push_back( comment ); - trx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); - sign( trx, _key ); - db->push_transaction( trx, 0 ); - trx.signatures.clear(); - trx.operations.clear(); -} - -void hf23_database_fixture::push_transaction( const operation& op, const fc::ecc::private_key& key ) +void sps_proposal_database_fixture::witness_vote( account_name_type _voter, account_name_type _witness, const fc::ecc::private_key& _key, bool _approve ) { signed_transaction tx; + account_witness_vote_operation op; + op.account = _voter; + op.witness = _witness; + op.approve = _approve; + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); tx.operations.push_back( op ); - sign( tx, key ); + sign( tx, _key ); db->push_transaction( tx, 0 ); } -void hf23_database_fixture::vest( const string& from, const string& to, const asset& amount, const fc::ecc::private_key& key ) +void sps_proposal_database_fixture::proxy( account_name_type _account, account_name_type _proxy, const fc::ecc::private_key& _key ) { - FC_ASSERT( amount.symbol == HIVE_SYMBOL, "Can only vest TESTS" ); - - transfer_to_vesting_operation op; - op.from = from; - op.to = to; - op.amount = amount; + signed_transaction tx; + account_witness_proxy_operation op; + op.account = _account; + op.proxy = _proxy; - push_transaction( op, key ); + tx.operations.push_back( op ); + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + sign( tx, _key ); + db->push_transaction( tx, 0 ); } void hf23_database_fixture::delegate_vest( const string& delegator, const string& delegatee, const asset& amount, const fc::ecc::private_key& key ) @@ -1146,15 +1258,6 @@ void hf23_database_fixture::delegate_vest( const string& delegator, const string push_transaction( op, key ); } -void delayed_vote_database_fixture::push_transaction( const operation& op, const fc::ecc::private_key& key ) -{ - signed_transaction tx; - tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); - tx.operations.push_back( op ); - sign( tx, key ); - db->push_transaction( tx, 0 ); -} - void delayed_vote_database_fixture::witness_vote( const std::string& account, const std::string& witness, const bool approve, const fc::ecc::private_key& key ) { account_witness_vote_operation op; @@ -1166,18 +1269,6 @@ void delayed_vote_database_fixture::witness_vote( const std::string& account, co push_transaction( op, key ); } -void delayed_vote_database_fixture::vest( const string& from, const string& to, const asset& amount, const fc::ecc::private_key& key ) -{ - FC_ASSERT( amount.symbol == HIVE_SYMBOL, "Can only vest TESTS" ); - - transfer_to_vesting_operation op; - op.from = from; - op.to = to; - op.amount = amount; - - push_transaction( op, key ); -} - void delayed_vote_database_fixture::decline_voting_rights( const string& account, const bool decline, const fc::ecc::private_key& key ) { decline_voting_rights_operation op; @@ -1264,17 +1355,17 @@ asset delayed_vote_database_fixture::to_vest( const asset& liquid, const bool to } template< typename COLLECTION > -fc::optional< size_t > delayed_vote_database_fixture::get_position_in_delayed_voting_array( const COLLECTION& collection, size_t day, size_t minutes ) +fc::optional< size_t > delayed_vote_database_fixture::get_position_in_delayed_voting_array( const COLLECTION& collection, size_t nr_interval, size_t seconds ) { if( collection.empty() ) return fc::optional< size_t >(); - auto time = collection[ 0 ].time + fc::days( day ) + fc::minutes( minutes ); + auto time = collection[ 0 ].time + fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS * nr_interval ) + fc::seconds( seconds ); size_t idx = 0; for( auto& item : collection ) { - auto end_of_day = item.time + fc::days( 1 ); + auto end_of_day = item.time + fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS ); if( end_of_day > time ) return idx; @@ -1315,7 +1406,7 @@ using bip_dvd_vector = chainbase::t_vector< delayed_votes_data >; template fc::optional< size_t > delayed_vote_database_fixture::get_position_in_delayed_voting_array< bip_dvd_vector >( const bip_dvd_vector& collection, size_t day, size_t minutes ); template bool delayed_vote_database_fixture::check_collection< dvd_vector >( const dvd_vector& collection, ushare_type idx, const fc::time_point_sec& time, const ushare_type val ); -#ifndef ENABLE_MIRA +#ifndef ENABLE_STD_ALLOCATOR template bool delayed_vote_database_fixture::check_collection< bip_dvd_vector >( const bip_dvd_vector& collection, ushare_type idx, const fc::time_point_sec& time, const ushare_type val ); #endif template bool delayed_vote_database_fixture::check_collection< delayed_voting::opt_votes_update_data_items >( const delayed_voting::opt_votes_update_data_items& collection, const bool withdraw_executor, const share_type val, const account_object& obj ); @@ -1354,6 +1445,8 @@ json_rpc_database_fixture::json_rpc_database_fixture() appbase::app().get_plugin< hive::plugins::condenser_api::condenser_api_plugin >().plugin_startup(); + rpc_plugin->finalize_startup(); + db = &appbase::app().get_plugin< hive::plugins::chain::chain_plugin >().db(); BOOST_REQUIRE( db ); @@ -1431,6 +1524,8 @@ void json_rpc_database_fixture::review_answer( fc::variant& answer, int64_t code fc::variant_object error; int64_t answer_code; + BOOST_TEST_MESSAGE( fc::json::to_string( answer ).c_str() ); + if( is_fail ) { if( id.valid() && code != JSON_RPC_INVALID_REQUEST ) diff --git a/tests/unit/db_fixture/database_fixture.hpp b/tests/unit/db_fixture/database_fixture.hpp index 2761487674..aed735cedf 100644 --- a/tests/unit/db_fixture/database_fixture.hpp +++ b/tests/unit/db_fixture/database_fixture.hpp @@ -66,27 +66,45 @@ extern uint32_t HIVE_TESTING_GENESIS_TIMESTAMP; << req_throw_info << std::endl; \ }*/ -#define HIVE_REQUIRE_THROW( expr, exc_type ) \ +#define HIVE_REQUIRE_THROW( expr, exc_type ) \ BOOST_REQUIRE_THROW( expr, exc_type ); -#define HIVE_CHECK_THROW( expr, exc_type ) \ -{ \ - std::string req_throw_info = fc::json::to_string( \ - fc::mutable_variant_object() \ - ("source_file", __FILE__) \ - ("source_lineno", __LINE__) \ - ("expr", #expr) \ - ("exc_type", #exc_type) \ - ); \ - if( fc::enable_record_assert_trip ) \ - std::cout << "HIVE_CHECK_THROW begin " \ +#define HIVE_CHECK_THROW( expr, exc_type ) \ +{ \ + std::string req_throw_info = fc::json::to_string( \ + fc::mutable_variant_object() \ + ("source_file", __FILE__) \ + ("source_lineno", __LINE__) \ + ("expr", #expr) \ + ("exc_type", #exc_type) \ + ); \ + if( fc::enable_record_assert_trip ) \ + std::cout << "HIVE_CHECK_THROW begin " \ << req_throw_info << std::endl; \ - BOOST_CHECK_THROW( expr, exc_type ); \ - if( fc::enable_record_assert_trip ) \ - std::cout << "HIVE_CHECK_THROW end " \ + BOOST_CHECK_THROW( expr, exc_type ); \ + if( fc::enable_record_assert_trip ) \ + std::cout << "HIVE_CHECK_THROW end " \ << req_throw_info << std::endl; \ } +#define HIVE_REQUIRE_ASSERT( expr, assert_test ) \ +do { \ + bool flag = fc::enable_record_assert_trip; \ + fc::enable_record_assert_trip = true; \ + HIVE_REQUIRE_THROW( expr, fc::assert_exception ); \ + BOOST_REQUIRE_EQUAL( fc::last_assert_expression, assert_test ); \ + fc::enable_record_assert_trip = flag; \ +} while(false) + +#define HIVE_REQUIRE_CHAINBASE_ASSERT( expr, assert_msg ) \ +do { \ + bool flag = fc::enable_record_assert_trip; \ + fc::enable_record_assert_trip = true; \ + HIVE_REQUIRE_THROW( expr, fc::exception ); \ + BOOST_REQUIRE_EQUAL( fc::last_assert_expression, assert_msg ); \ + fc::enable_record_assert_trip = flag; \ +} while(false) + #define REQUIRE_OP_VALIDATION_FAILURE_2( op, field, value, exc_type ) \ { \ const auto temp = op.field; \ @@ -126,16 +144,24 @@ extern uint32_t HIVE_TESTING_GENESIS_TIMESTAMP; const auto& name = account_create(BOOST_PP_STRINGIZE(name), name ## _public_key, name ## _post_key.get_public_key()); \ account_id_type name ## _id = name.get_id(); (void)name ## _id; -#define GET_ACTOR(name) \ - fc::ecc::private_key name ## _private_key = generate_private_key(BOOST_PP_STRINGIZE(name)); \ - const account_object& name = get_account(BOOST_PP_STRINGIZE(name)); \ - account_id_type name ## _id = name.get_id(); \ - (void)name ##_id - #define ACTORS_IMPL(r, data, elem) ACTOR(elem) #define ACTORS(names) BOOST_PP_SEQ_FOR_EACH(ACTORS_IMPL, ~, names) \ validate_database(); +#define PREP_ACTOR_EXT(object, name) \ + fc::ecc::private_key name ## _private_key = object.generate_private_key(BOOST_PP_STRINGIZE(name)); \ + fc::ecc::private_key name ## _post_key = object.generate_private_key(std::string( BOOST_PP_STRINGIZE(name) ) + "_post" ); \ + public_key_type name ## _public_key = name ## _private_key.get_public_key(); + +#define ACTOR_EXT(object, name) \ + PREP_ACTOR_EXT(object, name) \ + const auto& name = object.account_create(BOOST_PP_STRINGIZE(name), name ## _public_key, name ## _post_key.get_public_key()); \ + account_id_type name ## _id = name.get_id(); (void)name ## _id; + +#define ACTORS_EXT_IMPL(r, data, elem) ACTOR_EXT(data, elem) +#define ACTORS_EXT(object, names) BOOST_PP_SEQ_FOR_EACH(ACTORS_EXT_IMPL, object, names) \ + object.validate_database(); + #define SMT_SYMBOL( name, decimal_places, db ) \ asset_symbol_type name ## _symbol = get_new_smt_symbol( decimal_places, db ); @@ -231,7 +257,9 @@ struct database_fixture { */ void generate_blocks(fc::time_point_sec timestamp, bool miss_intermediate_blocks = true); + void generate_seconds_blocks( uint32_t seconds, bool skip_interm_blocks = true ); void generate_days_blocks( uint32_t days, bool skip_interm_blocks = true ); + fc::string get_current_time_iso_string() const; const account_object& account_create( @@ -263,14 +291,17 @@ struct database_fixture { const share_type& fee ); + void push_transaction( const operation& op, const fc::ecc::private_key& key ); + void fund( const string& account_name, const share_type& amount = 500000 ); - void fund( const string& account_name, const asset& amount ); + void fund( const string& account_name, const asset& amount, bool update_print_rate = true ); void transfer( const string& from, const string& to, const asset& amount ); void convert( const string& account_name, const asset& amount ); void vest( const string& from, const string& to, const asset& amount ); void vest( const string& from, const share_type& amount ); + void vest( const string& from, const string& to, const asset& amount, const fc::ecc::private_key& key ); void proxy( const string& account, const string& proxy ); - void set_price_feed( const price& new_price ); + void set_price_feed( const price& new_price, bool stop_at_update_block = false ); void set_witness_props( const flat_map< string, vector< char > >& new_props ); account_id_type get_account_id( const string& account_name )const; asset get_balance( const string& account_name )const; @@ -282,6 +313,17 @@ struct database_fixture { asset get_vesting( const string& account_name )const; asset get_vest_rewards( const string& account_name )const; asset get_vest_rewards_as_hive( const string& account_name )const; + +private: + + void post_comment_internal( const std::string& _author, const std::string& _permlink, const std::string& _title, const std::string& _body, const std::string& _parent_permlink, const fc::ecc::private_key& _key ); + +public: + + void post_comment_with_block_generation( std::string _author, std::string _permlink, std::string _title, std::string _body, std::string _parent_permlink, const fc::ecc::private_key& _key ); + void post_comment( std::string _author, std::string _permlink, std::string _title, std::string _body, std::string _parent_permlink, const fc::ecc::private_key& _key); + void vote( std::string _author, std::string _permlink, std::string _voter, int16_t _weight, const fc::ecc::private_key& _key ); + void sign( signed_transaction& trx, const fc::ecc::private_key& key ); vector< operation > get_last_operations( uint32_t ops ); @@ -291,11 +333,33 @@ struct database_fixture { struct clean_database_fixture : public database_fixture { - clean_database_fixture( uint16_t shared_file_size_in_mb = shared_file_size_in_mb_512 ); + clean_database_fixture( uint16_t shared_file_size_in_mb = shared_file_size_in_mb_512, fc::optional hardfork = fc::optional() ); virtual ~clean_database_fixture(); - void resize_shared_mem( uint64_t size ); + void resize_shared_mem( uint64_t size, fc::optional hardfork = fc::optional() ); void validate_database(); + void inject_hardfork( uint32_t hardfork ); +}; + +struct hardfork_database_fixture : public clean_database_fixture +{ + hardfork_database_fixture( uint16_t shared_file_size_in_mb = shared_file_size_in_mb_512, uint32_t hardfork = HIVE_BLOCKCHAIN_VERSION.minor_v() ); + virtual ~hardfork_database_fixture(); +}; + +struct cluster_database_fixture +{ + uint16_t shared_file_size_in_mb; + + using ptr_hardfork_database_fixture = std::unique_ptr; + + using content_method = std::function; + + cluster_database_fixture( uint16_t _shared_file_size_in_mb = database_fixture::shared_file_size_in_mb_512 ); + virtual ~cluster_database_fixture(); + + void execute_24( content_method content ); + void execute_25( content_method content ); }; struct live_database_fixture : public database_fixture @@ -358,7 +422,7 @@ struct sps_proposal_database_fixture : public virtual clean_database_fixture int64_t create_proposal( std::string creator, std::string receiver, time_point_sec start_date, time_point_sec end_date, - asset daily_pay, const fc::ecc::private_key& key ); + asset daily_pay, const fc::ecc::private_key& key, bool with_block_generation = true ); void vote_proposal( std::string voter, const std::vector< int64_t >& id_proposals, bool approve, const fc::ecc::private_key& key ); @@ -367,13 +431,14 @@ struct sps_proposal_database_fixture : public virtual clean_database_fixture void remove_proposal(account_name_type _deleter, flat_set _proposal_id, const fc::ecc::private_key& _key); - void update_proposal(uint64_t proposal_id, std::string creator, asset daily_pay, std::string subject, std::string permlink, const fc::ecc::private_key& key ); - bool find_vote_for_proposal(const std::string& _user, int64_t _proposal_id); + void update_proposal(uint64_t proposal_id, std::string creator, asset daily_pay, std::string subject, std::string permlink, const fc::ecc::private_key& key, time_point_sec* end_date = nullptr ); + bool find_vote_for_proposal(const std::string& _user, int64_t _proposal_id); uint64_t get_nr_blocks_until_maintenance_block(); uint64_t get_nr_blocks_until_daily_maintenance_block(); - void post_comment( std::string _authro, std::string _permlink, std::string _title, std::string _body, std::string _parent_permlink, const fc::ecc::private_key& _key); + void witness_vote( account_name_type _voter, account_name_type _witness, const fc::ecc::private_key& _key, bool _approve = true ); + void proxy( account_name_type _account, account_name_type _proxy, const fc::ecc::private_key& _key ); struct create_proposal_data { @@ -405,24 +470,20 @@ struct sps_proposal_database_fixture_performance : public sps_proposal_database_ : sps_proposal_database_fixture( shared_file_size_in_mb ) { db->get_benchmark_dumper().set_enabled( true ); - db->set_sps_remove_threshold( -1 ); + db_plugin->debug_update( []( database& db ) + { + db.set_remove_threshold( -1 ); + }); } }; struct hf23_database_fixture : public clean_database_fixture { - private: - - void push_transaction( const operation& op, const fc::ecc::private_key& key ); - - public: - hf23_database_fixture( uint16_t shared_file_size_in_mb = shared_file_size_in_mb_64 ) : clean_database_fixture( shared_file_size_in_mb ){} virtual ~hf23_database_fixture(){} - void vest( const string& from, const string& to, const asset& amount, const fc::ecc::private_key& key ); void delegate_vest( const string& delegator, const string& delegatee, const asset& amount, const fc::ecc::private_key& key ); }; @@ -436,10 +497,6 @@ struct hf24_database_fixture : public clean_database_fixture struct delayed_vote_database_fixture : public virtual clean_database_fixture { - private: - - void push_transaction( const operation& op, const fc::ecc::private_key& key ); - public: delayed_vote_database_fixture( uint16_t shared_file_size_in_mb = 8 ) @@ -447,7 +504,6 @@ struct delayed_vote_database_fixture : public virtual clean_database_fixture virtual ~delayed_vote_database_fixture(){} void witness_vote( const std::string& account, const std::string& witness, const bool approve, const fc::ecc::private_key& key ); - void vest( const string& from, const string& to, const asset& amount, const fc::ecc::private_key& key ); void withdraw_vesting( const string& account, const asset& amount, const fc::ecc::private_key& key ); void proxy( const string& account, const string& proxy, const fc::ecc::private_key& key ); void decline_voting_rights( const string& account, const bool decline, const fc::ecc::private_key& key ); diff --git a/tests/unit/plugin_tests/basic_plugin_tests.cpp b/tests/unit/plugin_tests/basic_plugin_tests.cpp index 3568fdebbd..a0e71d538d 100644 --- a/tests/unit/plugin_tests/basic_plugin_tests.cpp +++ b/tests/unit/plugin_tests/basic_plugin_tests.cpp @@ -1,4 +1,4 @@ -#if defined IS_TEST_NET && !defined ENABLE_MIRA && !defined ENABLE_STD_ALLOCATOR +#if defined IS_TEST_NET && !defined ENABLE_STD_ALLOCATOR #include #include @@ -22,44 +22,44 @@ BOOST_AUTO_TEST_CASE( plugin_object_size ) { try { - BOOST_CHECK_EQUAL( sizeof( account_by_key::key_lookup_object ), 56 ); + BOOST_CHECK_EQUAL( sizeof( account_by_key::key_lookup_object ), 56u ); //3.4M, lasting, expected 3*account_object on average - BOOST_CHECK_EQUAL( sizeof( account_history_rocksdb::volatile_operation_object ), 112 ); + BOOST_CHECK_EQUAL( sizeof( account_history_rocksdb::volatile_operation_object ), 112u ); //temporary, at most as many as operations in reversible blocks //BOOST_CHECK_EQUAL( sizeof( block_log_info::block_log_hash_state_object ), 0 ); //BOOST_CHECK_EQUAL( sizeof( block_log_info::block_log_pending_message_object ), 0 ); - BOOST_CHECK_EQUAL( sizeof( market_history::bucket_object ), 96 + BOOST_CHECK_EQUAL( sizeof( market_history::bucket_object ), 96u #ifdef HIVE_ENABLE_SMT + 8 #endif ); //temporary, regulated amount, ~13k - BOOST_CHECK_EQUAL( sizeof( market_history::order_history_object ), 88 ); + BOOST_CHECK_EQUAL( sizeof( market_history::order_history_object ), 88u ); //permanent, growing with executed limit_order_object, 2.5M atm - BOOST_CHECK_EQUAL( sizeof( rc::rc_resource_param_object ), 368 ); + BOOST_CHECK_EQUAL( sizeof( rc::rc_resource_param_object ), 368u ); //singleton - BOOST_CHECK_EQUAL( sizeof( rc::rc_pool_object ), 48 ); + BOOST_CHECK_EQUAL( sizeof( rc::rc_pool_object ), 48u ); //singleton - BOOST_CHECK_EQUAL( sizeof( rc::rc_account_object ), 64 ); + BOOST_CHECK_EQUAL( sizeof( rc::rc_account_object ), 64u ); //permanent, as many as account_object, 1.3M atm - BOOST_CHECK_EQUAL( sizeof( rc::rc_delegation_pool_object ), 40 ); + BOOST_CHECK_EQUAL( sizeof( rc::rc_delegation_pool_object ), 40u ); //unused - BOOST_CHECK_EQUAL( sizeof( rc::rc_indel_edge_object ), 48 ); + BOOST_CHECK_EQUAL( sizeof( rc::rc_indel_edge_object ), 48u ); //unused - BOOST_CHECK_EQUAL( sizeof( rc::rc_outdel_drc_edge_object ), 64 ); + BOOST_CHECK_EQUAL( sizeof( rc::rc_outdel_drc_edge_object ), 64u ); //unused - BOOST_CHECK_EQUAL( sizeof( reputation::reputation_object ), 32 ); + BOOST_CHECK_EQUAL( sizeof( reputation::reputation_object ), 32u ); //lasting, as many as account_object, 1.3M atm - BOOST_CHECK_EQUAL( sizeof( transaction_status::transaction_status_object ), 28 ); + BOOST_CHECK_EQUAL( sizeof( transaction_status::transaction_status_object ), 28u ); //temporary, depends on tracking flag, cuts out data from too old blocks - BOOST_CHECK_EQUAL( sizeof( witness::witness_custom_op_object ), 32 ); + BOOST_CHECK_EQUAL( sizeof( witness::witness_custom_op_object ), 32u ); //temporary, at most as many as account_object affected by custom ops in single block } FC_LOG_AND_RETHROW() diff --git a/tests/unit/plugin_tests/transaction_status.cpp b/tests/unit/plugin_tests/transaction_status.cpp index e1556fd094..a2cde3256c 100644 --- a/tests/unit/plugin_tests/transaction_status.cpp +++ b/tests/unit/plugin_tests/transaction_status.cpp @@ -389,7 +389,7 @@ BOOST_AUTO_TEST_CASE( transaction_status_test ) BOOST_TEST_MESSAGE( " -- transaction status expiration test" ); // The time of our last irreversible block - auto lib_time = db->fetch_block_by_number( db->get_dynamic_global_properties().last_irreversible_block_num )->timestamp; + auto lib_time = db->fetch_block_by_number( db->get_last_irreversible_block_num() )->timestamp; api_return = tx_status_api->api->find_transaction( { .transaction_id = transaction_id_type(), .expiration = lib_time } ); BOOST_REQUIRE( api_return.status == expired_irreversible ); BOOST_REQUIRE( api_return.block_num.valid() == false ); diff --git a/tests/unit/tests/automated_action_tests.cpp b/tests/unit/tests/automated_action_tests.cpp index d505e980ad..0590fe5fec 100644 --- a/tests/unit/tests/automated_action_tests.cpp +++ b/tests/unit/tests/automated_action_tests.cpp @@ -1,4 +1,4 @@ -#ifdef IS_TEST_NET +#if defined IS_TEST_NET && defined HIVE_ENABLE_SMT #include #include diff --git a/tests/unit/tests/basic_tests.cpp b/tests/unit/tests/basic_tests.cpp index 1e938d1942..34f0fb1562 100644 --- a/tests/unit/tests/basic_tests.cpp +++ b/tests/unit/tests/basic_tests.cpp @@ -56,23 +56,23 @@ BOOST_AUTO_TEST_CASE( parse_size_test ) BOOST_CHECK_THROW( fc::parse_size( "" ), fc::parse_error_exception ); BOOST_CHECK_THROW( fc::parse_size( "k" ), fc::parse_error_exception ); - BOOST_CHECK_EQUAL( fc::parse_size( "0" ), 0 ); - BOOST_CHECK_EQUAL( fc::parse_size( "1" ), 1 ); - BOOST_CHECK_EQUAL( fc::parse_size( "2" ), 2 ); - BOOST_CHECK_EQUAL( fc::parse_size( "3" ), 3 ); - BOOST_CHECK_EQUAL( fc::parse_size( "4" ), 4 ); - - BOOST_CHECK_EQUAL( fc::parse_size( "9" ), 9 ); - BOOST_CHECK_EQUAL( fc::parse_size( "10" ), 10 ); - BOOST_CHECK_EQUAL( fc::parse_size( "11" ), 11 ); - BOOST_CHECK_EQUAL( fc::parse_size( "12" ), 12 ); - - BOOST_CHECK_EQUAL( fc::parse_size( "314159265"), 314159265 ); - BOOST_CHECK_EQUAL( fc::parse_size( "1k" ), 1024 ); + BOOST_CHECK_EQUAL( fc::parse_size( "0" ), 0u ); + BOOST_CHECK_EQUAL( fc::parse_size( "1" ), 1u ); + BOOST_CHECK_EQUAL( fc::parse_size( "2" ), 2u ); + BOOST_CHECK_EQUAL( fc::parse_size( "3" ), 3u ); + BOOST_CHECK_EQUAL( fc::parse_size( "4" ), 4u ); + + BOOST_CHECK_EQUAL( fc::parse_size( "9" ), 9u ); + BOOST_CHECK_EQUAL( fc::parse_size( "10" ), 10u ); + BOOST_CHECK_EQUAL( fc::parse_size( "11" ), 11u ); + BOOST_CHECK_EQUAL( fc::parse_size( "12" ), 12u ); + + BOOST_CHECK_EQUAL( fc::parse_size( "314159265"), 314159265u ); + BOOST_CHECK_EQUAL( fc::parse_size( "1k" ), 1024u ); BOOST_CHECK_THROW( fc::parse_size( "1a" ), fc::parse_error_exception ); - BOOST_CHECK_EQUAL( fc::parse_size( "1kb" ), 1000 ); - BOOST_CHECK_EQUAL( fc::parse_size( "1MiB" ), 1048576 ); - BOOST_CHECK_EQUAL( fc::parse_size( "32G" ), 34359738368 ); + BOOST_CHECK_EQUAL( fc::parse_size( "1kb" ), 1000u ); + BOOST_CHECK_EQUAL( fc::parse_size( "1MiB" ), 1048576u ); + BOOST_CHECK_EQUAL( fc::parse_size( "32G" ), 34359738368u ); } /** @@ -434,21 +434,21 @@ BOOST_AUTO_TEST_CASE( curation_weight_test ) } -#if !defined ENABLE_MIRA && !defined ENABLE_STD_ALLOCATOR +#ifndef ENABLE_STD_ALLOCATOR BOOST_AUTO_TEST_CASE( chain_object_size ) { //typical elements of various objects - BOOST_CHECK_EQUAL( sizeof( account_object::id_type ), 4 ); //hidden first element of all objects (here just an example, all are the same size) - BOOST_CHECK_EQUAL( sizeof( account_id_type ), 4 ); //all id_refs are of the same size - BOOST_CHECK_EQUAL( sizeof( time_point_sec ), 4 ); - BOOST_CHECK_EQUAL( sizeof( share_type ), 8 ); - BOOST_CHECK_EQUAL( sizeof( HIVE_asset ), 8 ); //all tiny assets are of the same size - BOOST_CHECK_EQUAL( sizeof( asset ), 16 ); - BOOST_CHECK_EQUAL( sizeof( account_name_type ), 16 ); - BOOST_CHECK_EQUAL( sizeof( shared_string ), 32 ); //it has dynamic component as well - BOOST_CHECK_EQUAL( sizeof( price ), 32 ); - BOOST_CHECK_EQUAL( sizeof( t_vector< char > ), 32 ); //it has dynamic component as well, all vectors have the same static size - + BOOST_CHECK_EQUAL( sizeof( account_object::id_type ), 4u ); //hidden first element of all objects (here just an example, all are the same size) + BOOST_CHECK_EQUAL( sizeof( account_id_type ), 4u ); //all id_refs are of the same size + BOOST_CHECK_EQUAL( sizeof( time_point_sec ), 4u ); + BOOST_CHECK_EQUAL( sizeof( share_type ), 8u ); + BOOST_CHECK_EQUAL( sizeof( HIVE_asset ), 8u ); //all tiny assets are of the same size + BOOST_CHECK_EQUAL( sizeof( asset ), 16u ); + BOOST_CHECK_EQUAL( sizeof( account_name_type ), 16u ); + BOOST_CHECK_EQUAL( sizeof( shared_string ), 32u ); //it has dynamic component as well + BOOST_CHECK_EQUAL( sizeof( price ), 32u ); + BOOST_CHECK_EQUAL( sizeof( t_vector< char > ), 32u ); //it has dynamic component as well, all vectors have the same static size + BOOST_CHECK_EQUAL( sizeof( public_key_type ), 33u ); /* The purpose of this test is to make you think about the impact on RAM when you make changes in chain objects. Also somewhat helps in catching new problems with alignment (f.e. when you added a flag member and object @@ -458,52 +458,53 @@ BOOST_AUTO_TEST_CASE( chain_object_size ) */ //top RAM gluttons - BOOST_CHECK_EQUAL( sizeof( comment_object ), 36 ); //85M+ growing fast - BOOST_CHECK_EQUAL( sizeof( comment_content_object ), 104 ); //as many as comment_object, dynamic size matters the most, in HiveMind + BOOST_CHECK_EQUAL( sizeof( comment_object ), 36u ); //85M+ growing fast //permanent objects (no operation to remove) - BOOST_CHECK_EQUAL( sizeof( account_object ), 488 ); //1.3M+ - BOOST_CHECK_EQUAL( sizeof( account_metadata_object ), 72 ); //as many as account_object, but only FatNode (also to be moved to HiveMind) - BOOST_CHECK_EQUAL( sizeof( account_authority_object ), 248 ); //as many as account_object - BOOST_CHECK_EQUAL( sizeof( liquidity_reward_balance_object ), 48 ); //obsolete - only created/modified up to HF12 (683 objects) - BOOST_CHECK_EQUAL( sizeof( witness_object ), 352 ); //small but potentially as many as account_object + BOOST_CHECK_EQUAL( sizeof( account_object ), 424u ); //1.3M+ + BOOST_CHECK_EQUAL( sizeof( account_metadata_object ), 72u ); //as many as account_object, but only FatNode (also to be moved to HiveMind) + BOOST_CHECK_EQUAL( sizeof( account_authority_object ), 248u ); //as many as account_object + BOOST_CHECK_EQUAL( sizeof( liquidity_reward_balance_object ), 48u ); //obsolete - only created/modified up to HF12 (683 objects) + BOOST_CHECK_EQUAL( sizeof( witness_object ), 352u ); //small but potentially as many as account_object //lasting objects (operation to create and remove, but with potential to grow) - BOOST_CHECK_EQUAL( sizeof( vesting_delegation_object ), 64 ); //1M+ (potential of account_object squared !!!) - BOOST_CHECK_EQUAL( sizeof( withdraw_vesting_route_object ), 48 ); //45k (potential of 10*account_object) - BOOST_CHECK_EQUAL( sizeof( witness_vote_object ), 40 ); //450k (potential of 30*account_object) + BOOST_CHECK_EQUAL( sizeof( vesting_delegation_object ), 64u ); //1M+ (potential of account_object squared !!!) + BOOST_CHECK_EQUAL( sizeof( withdraw_vesting_route_object ), 48u ); //45k (potential of 10*account_object) + BOOST_CHECK_EQUAL( sizeof( witness_vote_object ), 40u ); //450k (potential of 30*account_object) //buffered objects (operation to create, op/vop to remove after certain time) - BOOST_CHECK_EQUAL( sizeof( transaction_object ), 64 ); //at most <1h> of transactions - BOOST_CHECK_EQUAL( sizeof( vesting_delegation_expiration_object ), 48 ); //at most <5d> of undelegates - BOOST_CHECK_EQUAL( sizeof( owner_authority_history_object ), 104 ); //at most <30d> of ownership updates - BOOST_CHECK_EQUAL( sizeof( account_recovery_request_object ), 104 ); //at most <1d> of account recoveries - BOOST_CHECK_EQUAL( sizeof( change_recovery_account_request_object ), 48 ); //at most <30d> of recovery account changes - BOOST_CHECK_EQUAL( sizeof( comment_cashout_object ), 200 //at most <7d> of unpaid comments (all comments prior to HF19) + BOOST_CHECK_EQUAL( sizeof( transaction_object ), 64u ); //at most <1h> of transactions + BOOST_CHECK_EQUAL( sizeof( vesting_delegation_expiration_object ), 48u ); //at most <5d> of undelegates + BOOST_CHECK_EQUAL( sizeof( owner_authority_history_object ), 104u ); //at most <30d> of ownership updates + BOOST_CHECK_EQUAL( sizeof( account_recovery_request_object ), 96u ); //at most <1d> of account recoveries + BOOST_CHECK_EQUAL( sizeof( change_recovery_account_request_object ), 40u ); //at most <30d> of recovery account changes + BOOST_CHECK_EQUAL( sizeof( comment_cashout_object ), 200u //at most <7d> of unpaid comments (all comments prior to HF19) #ifdef HIVE_ENABLE_SMT + 32 #endif ); - BOOST_CHECK_EQUAL( sizeof( comment_vote_object ), 48 ); //at most <7d> of votes on unpaid comments - BOOST_CHECK_EQUAL( sizeof( convert_request_object ), 56 ); //at most <3.5d> of conversion requests - BOOST_CHECK_EQUAL( sizeof( escrow_object ), 120 ); //small but potentially lasting forever, limited to 255*account_object - BOOST_CHECK_EQUAL( sizeof( savings_withdraw_object ), 104 ); //at most <3d> of saving withdrawals - BOOST_CHECK_EQUAL( sizeof( limit_order_object ), 80 ); //at most <28d> of limit orders - BOOST_CHECK_EQUAL( sizeof( decline_voting_rights_request_object ), 32 ); //at most <30d> of decline requests - BOOST_CHECK_EQUAL( sizeof( proposal_object ), 144 ); //potentially infinite, but costs a lot to make (especially after HF24) - BOOST_CHECK_EQUAL( sizeof( proposal_vote_object ), 32 ); //potentially infinite, but limited by account_object and time of proposal_object life + BOOST_CHECK_EQUAL( sizeof( comment_vote_object ), 48u ); //at most <7d> of votes on unpaid comments + BOOST_CHECK_EQUAL( sizeof( convert_request_object ), 24u ); //at most <3.5d> of conversion requests + BOOST_CHECK_EQUAL( sizeof( collateralized_convert_request_object ), 32u ); //at most <3.5d> of conversion requests + BOOST_CHECK_EQUAL( sizeof( escrow_object ), 120u ); //small but potentially lasting forever, limited to 255*account_object + BOOST_CHECK_EQUAL( sizeof( savings_withdraw_object ), 104u ); //at most <3d> of saving withdrawals + BOOST_CHECK_EQUAL( sizeof( limit_order_object ), 80u ); //at most <28d> of limit orders + BOOST_CHECK_EQUAL( sizeof( decline_voting_rights_request_object ), 32u ); //at most <30d> of decline requests + BOOST_CHECK_EQUAL( sizeof( proposal_object ), 144u ); //potentially infinite, but costs a lot to make (especially after HF24) + BOOST_CHECK_EQUAL( sizeof( proposal_vote_object ), 32u ); //potentially infinite, but limited by account_object and time of proposal_object life //singletons (size only affects performance, especially with MIRA) - BOOST_CHECK_EQUAL( sizeof( reward_fund_object ), 96 ); - BOOST_CHECK_EQUAL( sizeof( dynamic_global_property_object ), 344 + BOOST_CHECK_EQUAL( sizeof( reward_fund_object ), 96u ); + BOOST_CHECK_EQUAL( sizeof( dynamic_global_property_object ), 368u #ifdef HIVE_ENABLE_SMT + 16 #endif ); - BOOST_CHECK_EQUAL( sizeof( block_summary_object ), 24 ); //always 64k objects - BOOST_CHECK_EQUAL( sizeof( hardfork_property_object ), 120 ); - BOOST_CHECK_EQUAL( sizeof( feed_history_object ), 136 ); //dynamic size worth 7*24 of sizeof(price) - BOOST_CHECK_EQUAL( sizeof( witness_schedule_object ), 536 ); + BOOST_CHECK_EQUAL( sizeof( block_summary_object ), 24u ); //always 64k objects + BOOST_CHECK_EQUAL( sizeof( hardfork_property_object ), 120u ); + BOOST_CHECK_EQUAL( sizeof( feed_history_object ), 232u ); //dynamic size worth 7*24 of sizeof(price) + BOOST_CHECK_EQUAL( sizeof( witness_schedule_object ), 536u ); + BOOST_CHECK_EQUAL( sizeof( recurrent_transfer_object ), 72u ); //TODO: categorize and evaluate size potential of SMT related objects: //account_regular_balance_object diff --git a/tests/unit/tests/block_tests.cpp b/tests/unit/tests/block_tests.cpp index aac4b35dad..dc6100e822 100644 --- a/tests/unit/tests/block_tests.cpp +++ b/tests/unit/tests/block_tests.cpp @@ -90,7 +90,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) //BOOST_CHECK( cur_witness != prev_witness ); b = bp.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing); BOOST_CHECK( b.witness == cur_witness ); - uint32_t cutoff_height = db.get_dynamic_global_properties().last_irreversible_block_num; + uint32_t cutoff_height = db.get_last_irreversible_block_num(); if( cutoff_height >= 200 ) { auto block = db.fetch_block_by_number( cutoff_height ); @@ -107,26 +107,19 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks ) db._log_hardforks = false; open_test_database( db, data_dir.path() ); -#ifndef ENABLE_MIRA BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num() ); -#endif b = cutoff_block; for( uint32_t i = 0; i < 200; ++i ) { -#ifndef ENABLE_MIRA BOOST_CHECK( db.head_block_id() == b.id() ); -#else - BOOST_CHECK( i==0 || ( db.head_block_id() == b.id() ) ); -#endif + //witness_id_type prev_witness = b.witness; string cur_witness = db.get_scheduled_witness(1); //BOOST_CHECK( cur_witness != prev_witness ); b = bp.generate_block(db.get_slot_time(1), cur_witness, init_account_priv_key, database::skip_nothing); } -#ifndef ENABLE_MIRA BOOST_CHECK_EQUAL( db.head_block_num(), cutoff_block.block_num()+200 ); -#endif } } catch (fc::exception& e) { edump((e.to_detail_string())); @@ -229,22 +222,22 @@ BOOST_AUTO_TEST_CASE( fork_blocks ) //The two databases are on distinct forks now, but at the same height. Make a block on db2, make it invalid, then //pass it to db1 and assert that db1 doesn't switch to the new fork. signed_block good_block; - BOOST_CHECK_EQUAL(db1.head_block_num(), 13); - BOOST_CHECK_EQUAL(db2.head_block_num(), 13); + BOOST_CHECK_EQUAL(db1.head_block_num(), 13u); + BOOST_CHECK_EQUAL(db2.head_block_num(), 13u); { auto b = bp2.generate_block(db2.get_slot_time(1), db2.get_scheduled_witness(1), init_account_priv_key, database::skip_nothing); good_block = b; b.transactions.emplace_back(signed_transaction()); b.transactions.back().operations.emplace_back(transfer_operation()); b.sign( init_account_priv_key ); - BOOST_CHECK_EQUAL(b.block_num(), 14); + BOOST_CHECK_EQUAL(b.block_num(), 14u); HIVE_CHECK_THROW(PUSH_BLOCK( db1, b ), fc::exception); } - BOOST_CHECK_EQUAL(db1.head_block_num(), 13); + BOOST_CHECK_EQUAL(db1.head_block_num(), 13u); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db1_tip); // assert that db1 switches to new fork with good block - BOOST_CHECK_EQUAL(db2.head_block_num(), 14); + BOOST_CHECK_EQUAL(db2.head_block_num(), 14u); PUSH_BLOCK( db1, good_block ); BOOST_CHECK_EQUAL(db1.head_block_id().str(), db2.head_block_id().str()); } catch (fc::exception& e) { @@ -606,7 +599,7 @@ BOOST_FIXTURE_TEST_CASE( rsf_missed_blocks, clean_database_fixture ) "1111111111111111111111111111111111111111111111111111111111111111" "1111111111111111111111111111111111111111111111111111111111111111" ); - BOOST_CHECK_EQUAL( db->witness_participation_rate(), HIVE_100_PERCENT ); + BOOST_CHECK_EQUAL( db->witness_participation_rate(), static_cast(HIVE_100_PERCENT) ); BOOST_TEST_MESSAGE("Generating a block skipping 1" ); generate_block( ~database::skip_fork_db, init_account_priv_key, 1 ); @@ -719,7 +712,7 @@ BOOST_FIXTURE_TEST_CASE( skip_block, clean_database_fixture ) BOOST_REQUIRE( db->head_block_num() == 2 ); witness::block_producer bp( *db ); - int init_block_num = db->head_block_num(); + unsigned int init_block_num = db->head_block_num(); int miss_blocks = fc::minutes( 1 ).to_seconds() / HIVE_BLOCK_INTERVAL; auto witness = db->get_scheduled_witness( miss_blocks ); auto block_time = db->get_slot_time( miss_blocks ); @@ -796,29 +789,58 @@ BOOST_FIXTURE_TEST_CASE( hardfork_test, database_fixture ) BOOST_REQUIRE( db->has_hardfork( 0 ) ); BOOST_REQUIRE( !db->has_hardfork( HIVE_HARDFORK_0_1 ) ); + auto itr = db->get_index< account_history_index >().indices().get< by_id >().end(); + itr--; + + const auto last_ah_id = itr->get_id(); + BOOST_TEST_MESSAGE( "Generate a block and check hardfork is applied" ); generate_block(); string op_msg = "Testnet: Hardfork applied"; - auto itr = db->get_index< account_history_index >().indices().get< by_id >().end(); - itr--; + + itr = db->get_index< account_history_index >().indices().get< by_id >().upper_bound(last_ah_id); + /// Skip another producer_reward_op generated at last produced block + ++itr; + + ilog("Looked up AH-id: ${a}, found: ${i}", ("a", last_ah_id)("i", itr->get_id())); + + /// Original AH record points (by_id) actual operation data. We need to query for it again + const buffer_type& _serialized_op = db->get(itr->op).serialized_op; + auto last_op = fc::raw::unpack_from_vector< hive::chain::operation >(_serialized_op); BOOST_REQUIRE( db->has_hardfork( 0 ) ); BOOST_REQUIRE( db->has_hardfork( HIVE_HARDFORK_0_1 ) ); operation hardfork_vop = hardfork_operation( HIVE_HARDFORK_0_1 ); - BOOST_REQUIRE( get_last_operations( 1 )[0] == hardfork_vop ); - BOOST_REQUIRE( db->get(itr->op).timestamp == db->head_block_time() ); + BOOST_REQUIRE(last_op == hardfork_vop); + BOOST_REQUIRE(db->get(itr->op).timestamp == db->head_block_time()); BOOST_TEST_MESSAGE( "Testing hardfork is only applied once" ); generate_block(); - itr = db->get_index< account_history_index >().indices().get< by_id >().end(); - itr--; + auto processed_op = last_op; + + /// Continue (starting from last HF op position), but skip last HF op + for(++itr; itr != db->get_index< account_history_index >().indices().get< by_id >().end(); ++itr) + { + const auto& ahRecord = *itr; + const buffer_type& _serialized_op = db->get(ahRecord.op).serialized_op; + processed_op = fc::raw::unpack_from_vector< hive::chain::operation >(_serialized_op); + } + + /// There shall be no more hardfork ops after last one. + BOOST_REQUIRE(processed_op.which() != hardfork_vop.which()); BOOST_REQUIRE( db->has_hardfork( 0 ) ); BOOST_REQUIRE( db->has_hardfork( HIVE_HARDFORK_0_1 ) ); - BOOST_REQUIRE( get_last_operations( 1 )[0] == hardfork_vop ); + + /// Search again for pre-HF operation + itr = db->get_index< account_history_index >().indices().get< by_id >().upper_bound(last_ah_id); + /// Skip another producer_reward_op generated at last produced block + ++itr; + + /// Here last HF vop shall be pointed, with its original time. BOOST_REQUIRE( db->get(itr->op).timestamp == db->head_block_time() - HIVE_BLOCK_INTERVAL ); } diff --git a/tests/unit/tests/curation_reward_tests.cpp b/tests/unit/tests/curation_reward_tests.cpp new file mode 100644 index 0000000000..e1c74d0f73 --- /dev/null +++ b/tests/unit/tests/curation_reward_tests.cpp @@ -0,0 +1,2293 @@ +#include + +#include "../db_fixture/database_fixture.hpp" + +#include +#include + +using namespace hive; +using namespace hive::chain; +using namespace hive::protocol; +using fc::string; + +struct window_input_data +{ + uint32_t nr_voters = 0; + uint32_t interval = 0; + uint32_t amount = 0; +}; + +struct comment_reward_info +{ + share_type total_reward; + share_type author_tokens; + share_type curation_tokens; +}; + +struct reward_stat +{ + using rewards_stats = std::vector; + + uint64_t value = 0; + + public: + + static void display_comment_rewards( const comment_reward_info& obj ) + { + BOOST_TEST_MESSAGE( "calculated total reward : author-tokens : curation-tokens" ); + display_reward( obj.total_reward ); + display_reward( obj.author_tokens ); + display_reward( obj.curation_tokens ); + } + + static void display_reward( const share_type& reward ) + { + std::string str = std::to_string( reward.value ); + + BOOST_TEST_MESSAGE( str.c_str() ); + } + + static void display_stats( const rewards_stats& stats ) + { + std::string str = " stats: {"; + + for( size_t i = 0; i < stats.size(); ++i ) + { + str += std::to_string( stats[i].value ); + if( i < stats.size() - 1 ) + str += ","; + } + + str += "}"; + + BOOST_TEST_MESSAGE( str.c_str() ); + } + + static void check_phase( const rewards_stats& stats, std::function cmp ) + { + display_stats( stats ); + + for( auto& item : stats ) + { + BOOST_REQUIRE( cmp( item ) ); + } + } + + static void check_phases( const rewards_stats& stats_a, const rewards_stats& stats_b, std::function cmp ) + { + display_stats( stats_a ); + display_stats( stats_b ); + + auto iter_a = stats_a.begin(); + auto iter_b = stats_b.begin(); + + while( iter_a != stats_a.end() && iter_b != stats_b.end() ) + { + BOOST_REQUIRE( cmp( *iter_a, *iter_b ) ); + + ++iter_a; + ++iter_b; + } + } +}; + +struct curve_printer +{ + struct curve_item + { + time_point_sec time; + + uint64_t weight = 0; + uint32_t reward = 0; + + std::string account; + }; + + using curve_items_type = std::vector; + + time_point_sec start_time; + + curve_items_type curve_items; + + void clear() + { + start_time = time_point_sec(); + curve_items.clear(); + } +}; + +void print_all(std::ostream& stream, const curve_printer& cp) +{ + stream << "start_time: "< curve_printers; + + std::map authors; + std::map author_keys; + + std::deque voters; + std::deque voter_keys; + + clean_database_fixture& test_object; + chain::database& db; + + std::vector comment_rewards; + + boost::signals2::connection _comment_reward_con; + + void preparation( bool enter ) + { + if( !prepare ) + return; + + if( enter ) + { + configuration_data_copy = configuration_data; + configuration_data.set_hive_cashout_window_seconds( seven_days );/*7 days like in mainnet*/ + } + else + { + configuration_data = configuration_data_copy; + } + } + + curation_rewards_handler( clean_database_fixture& _test_object, chain::database& _db, bool _prepare = true ) + : prepare( _prepare ), test_object( _test_object ), db( _db ) + { + create_printer(); + + _comment_reward_con = db.add_comment_reward_handler( + [&]( const comment_reward_notification& note ) + { + comment_rewards.emplace_back( comment_reward_info{ note.total_reward, note.author_tokens, note.curation_tokens } ); + }, + appbase::app().get_plugin< hive::plugins::debug_node::debug_node_plugin >() + ); + + preparation( true/*enter*/ ); + } + + ~curation_rewards_handler() + { + preparation( false/*enter*/ ); + chain::util::disconnect_signal( _comment_reward_con ); + } + + void prepare_author( std::set _authors ) + { + BOOST_REQUIRE( !_authors.empty() ); + + for( auto author : _authors ) + { + if( author == 3 ) + { + ACTORS_EXT( test_object, (author3) ); + authors.insert( std::make_pair( 3, "author3" ) ); + author_keys.insert( std::make_pair( 3, key(author3) ) ); + } + if( author == 2 ) + { + ACTORS_EXT( test_object, (author2) ); + authors.insert( std::make_pair( 2, "author2" ) ); + author_keys.insert( std::make_pair( 2, key(author2) ) ); + } + if( author == 1 ) + { + ACTORS_EXT( test_object, (author1) ); + authors.insert( std::make_pair( 1, "author1" ) ); + author_keys.insert( std::make_pair( 1, key(author1) ) ); + } + if( author == 0 ) + { + ACTORS_EXT( test_object, (author0) ); + authors.insert( std::make_pair( 0, "author0" ) ); + author_keys.insert( std::make_pair( 0, key(author0) ) ); + } + } + } + + void prepare_voters_0_80() + { + ACTORS_EXT( test_object, + (aoa00)(aoa01)(aoa02)(aoa03)(aoa04)(aoa05)(aoa06)(aoa07)(aoa08)(aoa09) + (aoa10)(aoa11)(aoa12)(aoa13)(aoa14)(aoa15)(aoa16)(aoa17)(aoa18)(aoa19) + (aoa20)(aoa21)(aoa22)(aoa23)(aoa24)(aoa25)(aoa26)(aoa27)(aoa28)(aoa29) + (aoa30)(aoa31)(aoa32)(aoa33)(aoa34)(aoa35)(aoa36)(aoa37)(aoa38)(aoa39) + + (bob00)(bob01)(bob02)(bob03)(bob04)(bob05)(bob06)(bob07)(bob08)(bob09) + (bob10)(bob11)(bob12)(bob13)(bob14)(bob15)(bob16)(bob17)(bob18)(bob19) + (bob20)(bob21)(bob22)(bob23)(bob24)(bob25)(bob26)(bob27)(bob28)(bob29) + (bob30)(bob31)(bob32)(bob33)(bob34)(bob35)(bob36)(bob37)(bob38)(bob39) + ) + + auto _voters = + { + "aoa00", "aoa01", "aoa02", "aoa03", "aoa04", "aoa05", "aoa06", "aoa07", "aoa08", "aoa09", + "aoa10", "aoa11", "aoa12", "aoa13", "aoa14", "aoa15", "aoa16", "aoa17", "aoa18", "aoa19", + "aoa20", "aoa21", "aoa22", "aoa23", "aoa24", "aoa25", "aoa26", "aoa27", "aoa28", "aoa29", + "aoa30", "aoa31", "aoa32", "aoa33", "aoa34", "aoa35", "aoa36", "aoa37", "aoa38", "aoa39", + + "bob00", "bob01", "bob02", "bob03", "bob04", "bob05", "bob06", "bob07", "bob08", "bob09", + "bob10", "bob11", "bob12", "bob13", "bob14", "bob15", "bob16", "bob17", "bob18", "bob19", + "bob20", "bob21", "bob22", "bob23", "bob24", "bob25", "bob26", "bob27", "bob28", "bob29", + "bob30", "bob31", "bob32", "bob33", "bob34", "bob35", "bob36", "bob37", "bob38", "bob39" + }; + std::copy( _voters.begin(), _voters.end(), std::back_inserter( voters ) ); + + auto _voter_keys = + { + key(aoa00), key(aoa01), key(aoa02), key(aoa03), key(aoa04), key(aoa05), key(aoa06), key(aoa07), key(aoa08), key(aoa09), + key(aoa10), key(aoa11), key(aoa12), key(aoa13), key(aoa14), key(aoa15), key(aoa16), key(aoa17), key(aoa18), key(aoa19), + key(aoa20), key(aoa21), key(aoa22), key(aoa23), key(aoa24), key(aoa25), key(aoa26), key(aoa27), key(aoa28), key(aoa29), + key(aoa30), key(aoa31), key(aoa32), key(aoa33), key(aoa34), key(aoa35), key(aoa36), key(aoa37), key(aoa38), key(aoa39), + + key(bob00), key(bob01), key(bob02), key(bob03), key(bob04), key(bob05), key(bob06), key(bob07), key(bob08), key(bob09), + key(bob10), key(bob11), key(bob12), key(bob13), key(bob14), key(bob15), key(bob16), key(bob17), key(bob18), key(bob19), + key(bob20), key(bob21), key(bob22), key(bob23), key(bob24), key(bob25), key(bob26), key(bob27), key(bob28), key(bob29), + key(bob30), key(bob31), key(bob32), key(bob33), key(bob34), key(bob35), key(bob36), key(bob37), key(bob38), key(bob39) + }; + std::copy( _voter_keys.begin(), _voter_keys.end(), std::back_inserter( voter_keys ) ); + } + + void prepare_voters_80_160() + { + ACTORS_EXT( test_object, + (coc00)(coc01)(coc02)(coc03)(coc04)(coc05)(coc06)(coc07)(coc08)(coc09) + (coc10)(coc11)(coc12)(coc13)(coc14)(coc15)(coc16)(coc17)(coc18)(coc19) + (coc20)(coc21)(coc22)(coc23)(coc24)(coc25)(coc26)(coc27)(coc28)(coc29) + (coc30)(coc31)(coc32)(coc33)(coc34)(coc35)(coc36)(coc37)(coc38)(coc39) + + (dod00)(dod01)(dod02)(dod03)(dod04)(dod05)(dod06)(dod07)(dod08)(dod09) + (dod10)(dod11)(dod12)(dod13)(dod14)(dod15)(dod16)(dod17)(dod18)(dod19) + (dod20)(dod21)(dod22)(dod23)(dod24)(dod25)(dod26)(dod27)(dod28)(dod29) + (dod30)(dod31)(dod32)(dod33)(dod34)(dod35)(dod36)(dod37)(dod38)(dod39) + ) + + auto _voters = + { + "coc00", "coc01", "coc02", "coc03", "coc04", "coc05", "coc06", "coc07", "coc08", "coc09", + "coc10", "coc11", "coc12", "coc13", "coc14", "coc15", "coc16", "coc17", "coc18", "coc19", + "coc20", "coc21", "coc22", "coc23", "coc24", "coc25", "coc26", "coc27", "coc28", "coc29", + "coc30", "coc31", "coc32", "coc33", "coc34", "coc35", "coc36", "coc37", "coc38", "coc39", + + "dod00", "dod01", "dod02", "dod03", "dod04", "dod05", "dod06", "dod07", "dod08", "dod09", + "dod10", "dod11", "dod12", "dod13", "dod14", "dod15", "dod16", "dod17", "dod18", "dod19", + "dod20", "dod21", "dod22", "dod23", "dod24", "dod25", "dod26", "dod27", "dod28", "dod29", + "dod30", "dod31", "dod32", "dod33", "dod34", "dod35", "dod36", "dod37", "dod38", "dod39" + }; + std::copy( _voters.begin(), _voters.end(), std::back_inserter( voters ) ); + + auto _voter_keys = + { + key(coc00), key(coc01), key(coc02), key(coc03), key(coc04), key(coc05), key(coc06), key(coc07), key(coc08), key(coc09), + key(coc10), key(coc11), key(coc12), key(coc13), key(coc14), key(coc15), key(coc16), key(coc17), key(coc18), key(coc19), + key(coc20), key(coc21), key(coc22), key(coc23), key(coc24), key(coc25), key(coc26), key(coc27), key(coc28), key(coc29), + key(coc30), key(coc31), key(coc32), key(coc33), key(coc34), key(coc35), key(coc36), key(coc37), key(coc38), key(coc39), + + key(dod00), key(dod01), key(dod02), key(dod03), key(dod04), key(dod05), key(dod06), key(dod07), key(dod08), key(dod09), + key(dod10), key(dod11), key(dod12), key(dod13), key(dod14), key(dod15), key(dod16), key(dod17), key(dod18), key(dod19), + key(dod20), key(dod21), key(dod22), key(dod23), key(dod24), key(dod25), key(dod26), key(dod27), key(dod28), key(dod29), + key(dod30), key(dod31), key(dod32), key(dod33), key(dod34), key(dod35), key(dod36), key(dod37), key(dod38), key(dod39) + }; + std::copy( _voter_keys.begin(), _voter_keys.end(), std::back_inserter( voter_keys ) ); + + } + + void prepare_voters() + { + prepare_voters_0_80(); + prepare_voters_80_160(); + } + + void prepare_10_voters() + { + ACTORS_EXT( test_object, + (aoa00)(aoa01)(aoa02)(aoa03)(aoa04)(aoa05)(aoa06)(aoa07)(aoa08)(aoa09) + ) + + auto _voters = + { + "aoa00", "aoa01", "aoa02", "aoa03", "aoa04", "aoa05", "aoa06", "aoa07", "aoa08", "aoa09" + }; + std::copy( _voters.begin(), _voters.end(), std::back_inserter( voters ) ); + + auto _voter_keys = + { + key(aoa00), key(aoa01), key(aoa02), key(aoa03), key(aoa04), key(aoa05), key(aoa06), key(aoa07), key(aoa08), key(aoa09) + }; + std::copy( _voter_keys.begin(), _voter_keys.end(), std::back_inserter( voter_keys ) ); + + } + + void prepare_funds_impl( uint32_t idx, uint32_t amount ) + { + test_object.fund( voters[idx], asset( amount, HIVE_SYMBOL ) ); + test_object.vest( voters[idx], voters[idx], asset( amount / 10, HIVE_SYMBOL ), voter_keys[idx] ); + } + + void prepare_funds( uint32_t& counter, const window_input_data& window ) + { + for( uint32_t i = counter; i < counter + window.nr_voters; ++i ) + { + prepare_funds_impl( i, window.amount ); + } + + counter += window.nr_voters; + } + + void prepare_funds( const window_input_data& early, const window_input_data& mid, const window_input_data& late ) + { + BOOST_REQUIRE_GE( voters.size(), early.nr_voters + mid.nr_voters + late.nr_voters ); + + uint32_t counter = 0; + + prepare_funds( counter, early ); + prepare_funds( counter, mid ); + prepare_funds( counter, late ); + } + + void prepare_funds( uint32_t amount = curation_rewards_handler::default_amount ) + { + for( uint32_t i = 0; i < voters.size(); ++i ) + { + prepare_funds_impl( i, amount ); + } + } + + void prepare_comment( const std::string& permlink, uint32_t creator_number ) + { + auto found_keys = author_keys.find( creator_number ); + BOOST_REQUIRE( found_keys != author_keys.end() ); + + auto found_author = authors.find( creator_number ); + BOOST_REQUIRE( found_author != authors.end() ); + + test_object.post_comment( found_author->second, permlink, "title", "body", "test", found_keys->second ); + } + + void create_printer() + { + curve_printers.emplace_back( curve_printer() ); + } + + void clear_printer( uint32_t comment_idx = 0 ) + { + BOOST_REQUIRE_LT( comment_idx, curve_printers.size() ); + + curve_printers[ comment_idx ].clear(); + } + + const curve_printer& get_printer( uint32_t comment_idx = 0 ) const + { + BOOST_REQUIRE_LT( comment_idx, curve_printers.size() ); + return curve_printers[ comment_idx ]; + } + + void set_start_time( const time_point_sec& _time, uint32_t comment_idx = 0 ) + { + BOOST_REQUIRE_LT( comment_idx, curve_printers.size() ); + + curve_printers[ comment_idx ].start_time = _time; + } + + void voting( size_t& vote_counter, uint32_t author_number, const std::string& permlink, const std::vector& votes_time, uint32_t comment_idx = 0 ) + { + if( votes_time.empty() ) + return; + + BOOST_REQUIRE_LT( comment_idx, curve_printers.size() ); + BOOST_REQUIRE_GE( voters.size() + vote_counter, votes_time.size() ); + + auto found_author = authors.find( author_number ); + BOOST_REQUIRE( found_author != authors.end() ); + + auto author = found_author->second; + + uint32_t vote_percent = HIVE_1_PERCENT * 90; + + auto comment_id = db.get_comment( author, permlink ).get_id(); + + for( auto& time : votes_time ) + { + if( time ) + test_object.generate_blocks( db.head_block_time() + fc::seconds( time ) ); + + test_object.vote( author, permlink, voters[ vote_counter ], vote_percent, voter_keys[ vote_counter ] ); + + auto& cvo = db.get< comment_vote_object, by_comment_voter >( boost::make_tuple( comment_id, test_object.get_account_id( voters[ vote_counter ] ) ) ); + + curve_printers[ comment_idx ].curve_items.emplace_back( curve_printer::curve_item{ db.head_block_time(), cvo.weight, 0, voters[ vote_counter ] } ); + + ++vote_counter; + } + } + + void curation_gathering( reward_stat::rewards_stats& early_stats, reward_stat::rewards_stats& mid_stats, reward_stat::rewards_stats& late_stats, uint32_t comment_idx = 0 ) + { + BOOST_REQUIRE_LT( comment_idx, curve_printers.size() ); + + const auto& dgpo = db.get_dynamic_global_properties(); + + for( auto& item : curve_printers[ comment_idx ].curve_items ) + { + const auto& acc = db.get_account( item.account ); + item.reward = static_cast( acc.curation_rewards.value ); + + uint64_t _seconds = static_cast( ( item.time - curve_printers[ comment_idx ].start_time ).to_seconds() ); + + if( _seconds >= dgpo.early_voting_seconds ) + { + if( _seconds < ( dgpo.early_voting_seconds + dgpo.mid_voting_seconds ) ) + mid_stats.emplace_back( reward_stat{ item.reward } ); + else + late_stats.emplace_back( reward_stat{ item.reward } ); + } + else + { + early_stats.emplace_back( reward_stat{ item.reward } ); + } + } + } + + void curation_gathering( reward_stat::rewards_stats& early_stats, reward_stat::rewards_stats& late_stats, uint32_t comment_idx = 0 ) + { + BOOST_REQUIRE_LT( comment_idx, curve_printers.size() ); + + const auto& dgpo = db.get_dynamic_global_properties(); + + for( auto& item : curve_printers[ comment_idx ].curve_items ) + { + const auto& acc = db.get_account( item.account ); + item.reward = static_cast( acc.curation_rewards.value ); + + uint64_t _seconds = static_cast( ( item.time - curve_printers[ comment_idx ].start_time ).to_seconds() ); + + if( _seconds >= dgpo.reverse_auction_seconds ) + { + late_stats.emplace_back( reward_stat{ item.reward } ); + } + else + { + early_stats.emplace_back( reward_stat{ item.reward } ); + } + } + } + + void make_payment( uint32_t back_offset_blocks = 0, uint32_t comment_idx = 0 ) + { + BOOST_REQUIRE_LT( comment_idx, curve_printers.size() ); + + auto _time = db.head_block_time(); + uint64_t _seconds = static_cast( ( _time - curve_printers[ comment_idx ].start_time ).to_seconds() ); + _seconds += HIVE_BLOCK_INTERVAL; + + _seconds += back_offset_blocks * HIVE_BLOCK_INTERVAL; + + if( seven_days > _seconds ) + test_object.generate_blocks( _time + fc::seconds( seven_days - _seconds ) ); + } + + share_type calculate_reward() + { + const auto& props = db.get_dynamic_global_properties(); + + int64_t start_inflation_rate = int64_t( HIVE_INFLATION_RATE_START_PERCENT ); + int64_t inflation_rate_adjustment = int64_t( db.head_block_num() / HIVE_INFLATION_NARROWING_PERIOD ); + int64_t inflation_rate_floor = int64_t( HIVE_INFLATION_RATE_STOP_PERCENT ); + + // below subtraction cannot underflow int64_t because inflation_rate_adjustment is <2^32 + int64_t current_inflation_rate = std::max( start_inflation_rate - inflation_rate_adjustment, inflation_rate_floor ); + + auto new_hive = ( props.virtual_supply.amount * current_inflation_rate ) / ( int64_t( HIVE_100_PERCENT ) * int64_t( HIVE_BLOCKS_PER_YEAR ) ); + return ( new_hive * props.content_reward_percent ) / HIVE_100_PERCENT; + } + + share_type current_total_reward() + { + const auto& reward_idx = db.get_index< reward_fund_index, by_id >(); + + auto fund_id = db.get_reward_fund().get_id(); + + auto found = reward_idx.find( fund_id ); + if( found == reward_idx.end() ) + return 0; + + return found->reward_balance.amount; + } + +}; + +BOOST_FIXTURE_TEST_SUITE( curation_reward_tests, clean_database_fixture ) + +void basic_test_impl( + const std::string& test_name, + clean_database_fixture& mgr_test, + const window_input_data& early, + const window_input_data& mid, + const window_input_data& late, + uint32_t reward, const fc::optional& offset = fc::optional() ) +{ + curation_rewards_handler crh( mgr_test, *( mgr_test.db ) ); + + crh.prepare_author( { 0 } ); + crh.prepare_voters(); + mgr_test.generate_block(); + + mgr_test.set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + mgr_test.generate_block(); + + crh.prepare_funds( early, mid, late ); + mgr_test.generate_block(); + + uint32_t author_number = 0; + std::string permlink = "somethingpermlink"; + + crh.prepare_comment( permlink, author_number ); + mgr_test.generate_block(); + + crh.set_start_time( mgr_test.db->head_block_time() ); + + if( offset.valid() ) + mgr_test.generate_blocks( mgr_test.db->head_block_time() + fc::seconds( *offset ) ); + + std::vector early_votes_time( early.nr_voters, early.interval ); + std::vector mid_votes_time( mid.nr_voters, mid.interval ); + std::vector late_votes_time( late.nr_voters, late.interval ); + + size_t vote_counter = 0; + crh.voting( vote_counter, author_number, permlink, early_votes_time ); + crh.voting( vote_counter, author_number, permlink, mid_votes_time ); + crh.voting( vote_counter, author_number, permlink, late_votes_time ); + crh.make_payment(); + + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats mid_stats; + reward_stat::rewards_stats late_stats; + + crh.curation_gathering( early_stats, mid_stats, late_stats ); + + auto cmp = [ reward ]( const reward_stat::rewards_stats& stats_a, const reward_stat::rewards_stats& stats_b, uint32_t factor ) + { + BOOST_REQUIRE( factor == 2 || factor == 4 ); + + auto _cmp = [ reward, factor ]( const reward_stat& item_a, const reward_stat& item_b ) + { + if( factor == 2 ) + return item_a.value == reward && ( item_a.value / factor == item_b.value ); + else + return item_a.value && ( item_a.value / factor == item_b.value ); + }; + reward_stat::check_phases( stats_a, stats_b, _cmp ); + }; + + { + BOOST_TEST_MESSAGE( "Comparison phases: `early` and `mid`" ); + cmp( early_stats, mid_stats, 2/*factor*/ ); + if( early_stats.empty() ^ mid_stats.empty() ) + { + if( !early_stats.empty() ) + BOOST_REQUIRE_EQUAL( early_stats[0].value, reward ); + else + BOOST_REQUIRE_EQUAL( mid_stats[0].value, reward ); + } + } + { + BOOST_TEST_MESSAGE( "Comparison phases: `mid` and `late`" ); + cmp( mid_stats, late_stats, 4/*factor*/ ); + if( mid_stats.empty() ^ late_stats.empty() ) + { + if( !mid_stats.empty() ) + { + if( early_stats.empty() ) + BOOST_REQUIRE_EQUAL( mid_stats[0].value, reward ); + else + BOOST_REQUIRE_EQUAL( mid_stats[0].value, reward / 2 ); + } + else + { + if( early_stats.empty() && mid_stats.empty() ) + BOOST_REQUIRE_EQUAL( late_stats[0].value, reward ); + else if( mid_stats.empty() ) + BOOST_REQUIRE_EQUAL( late_stats[0].value, reward / 4 ); + else + BOOST_REQUIRE_EQUAL( late_stats[0].value, reward / 8 ); + } + } + } + + print_all( std::cout, crh.get_printer() ); + print( std::cout, crh.get_printer() ); + + std::ofstream file( test_name ); + print( file, crh.get_printer() ); + + mgr_test.validate_database(); +} + +BOOST_AUTO_TEST_CASE( basic_test_v0 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Voting in every window." ); + + auto _a = curation_rewards_handler::default_amount; + + basic_test_impl( "00.content_format.time:curation_reward.csv", + *this, + window_input_data{ 25/*nr_voters*/, 12/*interval*/ , _a }/*early*/, + window_input_data{ 39/*nr_voters*/, 4480/*interval*/ , _a }/*mid*/, + window_input_data{ 95/*nr_voters*/, 4480/*interval*/ , _a }/*late*/, 645/*reward*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( basic_test_v1 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Voting in 2 windows: early, mid." ); + + auto _a = curation_rewards_handler::default_amount; + + basic_test_impl( "01.content_format.time:curation_reward.csv", + *this, + window_input_data{ 25/*nr_voters*/, 12/*interval*/ , _a }/*early*/, + window_input_data{ 39/*nr_voters*/, 4479/*interval*/ , _a }/*mid*/, + window_input_data()/*late*/, 760/*reward*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( basic_test_v2 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Voting in 2 windows: early, mid." ); + + auto _a = curation_rewards_handler::default_amount; + + basic_test_impl( "02.content_format.time:curation_reward.csv", + *this, + window_input_data{ 25/*nr_voters*/, 12/*interval*/ , _a }/*early*/, + window_input_data{ 134/*nr_voters*/, 1930/*interval*/ , _a }/*mid*/, + window_input_data()/*late*/, 411/*reward*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( basic_test_v3 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Voting in early window." ); + + auto _a = curation_rewards_handler::default_amount; + + basic_test_impl( "03.content_format.time:curation_reward.csv", + *this, + window_input_data{ 50/*nr_voters*/, 1720/*interval*/, _a }/*early*/, + window_input_data()/*mid*/, + window_input_data()/*late*/, 805/*reward*/, 12/*offset*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( basic_test_v4 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Voting in mid window." ); + + auto _a = curation_rewards_handler::default_amount; + + basic_test_impl( "04.content_format.time:curation_reward.csv", + *this, + window_input_data()/*early*/, + window_input_data{ 50/*nr_voters*/, 300/*interval*/, _a }/*mid*/, + window_input_data()/*late*/, 805/*reward*/, 25*3600/*offset*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( basic_test_v5 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Voting in early window." ); + + auto _a = curation_rewards_handler::default_amount * 2; + + basic_test_impl( "05.content_format.time:curation_reward.csv", + *this, + window_input_data{ 50/*nr_voters*/, 1720/*interval*/, _a }/*early*/, + window_input_data()/*mid*/, + window_input_data()/*late*/, 805/*reward*/, 12/*offset*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( basic_test_v6 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Voting in mid window." ); + + auto _a = curation_rewards_handler::default_amount * 2; + + basic_test_impl( "06.content_format.time:curation_reward.csv", + *this, + window_input_data()/*early*/, + window_input_data{ 50/*nr_voters*/, 300/*interval*/, _a }/*mid*/, + window_input_data()/*late*/, 805/*reward*/, 25*3600/*offset*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( basic_test_v7 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days)." ); + BOOST_TEST_MESSAGE( "Totally 50 voters vote on 1 comment" ); + BOOST_TEST_MESSAGE( "1 voter votes in early window, rest of voters vote in mid window." ); + + auto _a = curation_rewards_handler::default_amount * 2; + + basic_test_impl( "07.content_format.time:curation_reward.csv", + *this, + window_input_data{ 1/*nr_voters*/, 12/*interval*/, _a }/*early*/, + window_input_data{ 49/*nr_voters*/, 300/*interval*/, _a }/*mid*/, + window_input_data()/*late*/, 1579/*reward*/, 24*3600 - 90/*offset*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( basic_test_v8 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days)." ); + BOOST_TEST_MESSAGE( "Totally 50 voters vote on 1 comment" ); + BOOST_TEST_MESSAGE( "5 voters vote in early window, rest of voters vote in mid window." ); + + auto _a = curation_rewards_handler::default_amount * 2; + + basic_test_impl( "08.content_format.time:curation_reward.csv", + *this, + window_input_data{ 5/*nr_voters*/, 12/*interval*/, _a }/*early*/, + window_input_data{ 45/*nr_voters*/, 300/*interval*/, _a }/*mid*/, + window_input_data()/*late*/, 1464/*reward*/, 24*3600 - 90/*offset*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( no_votes ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Lack of votes." ); + + curation_rewards_handler crh( *this, *db ); + + crh.prepare_author( { 0 } ); + generate_block(); + + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + uint32_t author_number = 0; + std::string permlink = "somethingpermlink"; + + crh.prepare_comment( permlink, author_number ); + generate_block(); + + generate_blocks( db->head_block_time() + fc::seconds( 60*60*24*7/*7 days like in mainnet*/ ) ); + + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats mid_stats; + reward_stat::rewards_stats late_stats; + + crh.curation_gathering( early_stats, mid_stats, late_stats ); + + auto found_author = crh.authors.find( author_number ); + BOOST_REQUIRE( found_author != crh.authors.end() ); + const auto& creator = db->get_account( found_author->second ); + BOOST_REQUIRE_EQUAL( creator.posting_rewards.value, 0 ); + + auto cmp = []( const reward_stat& item ) + { + return item.value == 0; + }; + reward_stat::check_phase( early_stats, cmp ); + reward_stat::check_phase( mid_stats, cmp ); + reward_stat::check_phase( late_stats, cmp ); + + print_all( std::cout, crh.get_printer() ); + print( std::cout, crh.get_printer() ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( one_vote_for_comment ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Only 1 vote per 1 comment." ); + + curation_rewards_handler crh( *this, *db ); + + crh.prepare_author( { 0, 1, 2 } ); + crh.prepare_10_voters(); + generate_block(); + + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + crh.prepare_funds(); + generate_block(); + + std::string permlink = "somethingpermlink"; + + for( uint32_t i = 0 ; i < 3; ++i ) + crh.prepare_comment( permlink, i/*author_number*/ ); + generate_block(); + + crh.set_start_time( db->head_block_time() ); + + //Every comment has only 1 vote, but votes are created in different windows. + size_t vote_counter = 0; + + uint32_t vote_time_early_window = 12; + uint32_t vote_time_mid_window = crh.early_window; // crh.early_window + 12 + uint32_t vote_time_late_window = crh.mid_window; // crh.early_window + 12 + crh.mid_window + + crh.voting( vote_counter, 0/*author_number*/, permlink, { vote_time_early_window } ); + crh.voting( vote_counter, 1/*author_number*/, permlink, { vote_time_mid_window } ); + crh.voting( vote_counter, 2/*author_number*/, permlink, { vote_time_late_window } ); + crh.make_payment(); + + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats mid_stats; + reward_stat::rewards_stats late_stats; + + crh.curation_gathering( early_stats, mid_stats, late_stats ); + /* + Description: + vc0 - vote for comment_0 + vc1 - vote for comment_1 + vc2 - vote for comment_2 + + |*****early_window*****|*****mid_window*****|*****late_window*****| + vc0 + vc1 + vc2 + + Results: + vc0 == vc1 == vc2 + */ + + { + auto cmp = []( const reward_stat& item_a, const reward_stat& item_b ) + { + return item_a.value == 12463 && item_a.value == item_b.value; + }; + reward_stat::check_phases( early_stats, mid_stats, cmp ); + reward_stat::check_phases( mid_stats, late_stats, cmp ); + } + + print_all( std::cout, crh.get_printer() ); + print( std::cout, crh.get_printer() ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( two_votes_for_comment ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards after HF25. Reward during whole rewards-time (7 days). Only 2 votes per 1 comment." ); + + curation_rewards_handler crh( *this, *db ); + + crh.prepare_author( { 0, 1, 2 } ); + crh.prepare_10_voters(); + generate_block(); + + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + crh.prepare_funds(); + generate_block(); + + std::string permlink = "somethingpermlink"; + + for( uint32_t i = 0 ; i < 3; ++i ) + crh.prepare_comment( permlink, i/*author_number*/ ); + generate_block(); + + crh.set_start_time( db->head_block_time() ); + + /* + Every comment has only 2 votes in different windows. + comment_0: early_window, mid_window + comment_1: early_window, late_window + comment_2: mid_window, late_window + */ + size_t vote_counter = 0; + + uint32_t vote_time_early_window = 3600; + uint32_t vote_time_mid_window = crh.early_window; // crh.early_window + 3600 + uint32_t vote_time_late_window = crh.mid_window + 3600; // crh.early_window + 3600 + crh.mid_window + 3600 + + //Both votes are in the same block. + crh.voting( vote_counter, 0/*author_number*/, permlink, { vote_time_early_window } ); + crh.voting( vote_counter, 1/*author_number*/, permlink, { 0 } ); + + //Both votes are in the same block. + crh.voting( vote_counter, 0/*author_number*/, permlink, { vote_time_mid_window } ); + crh.voting( vote_counter, 2/*author_number*/, permlink, { 0 } ); + + //Both votes are in the same block. + crh.voting( vote_counter, 1/*author_number*/, permlink, { vote_time_late_window } ); + crh.voting( vote_counter, 2/*author_number*/, permlink, { 0 } ); + + crh.make_payment(); + + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats mid_stats; + reward_stat::rewards_stats late_stats; + + crh.curation_gathering( early_stats, mid_stats, late_stats ); + /* + Description: + vc0a - vote(a) for comment_0 vc0b - vote(b) for comment_0 + vc1a - vote(a) for comment_1 vc1b - vote(b) for comment_1 + vc2a - vote(a) for comment_2 vc2b - vote(b) for comment_2 + + |*****early_window*****|*****mid_window*****|*****late_window*****| + vc0a vc0b + vc1a vc1b + vc2a vc2b + + Results: + sum( vc0a + vc0b ) == sum( vc1a + vc1b ) == sum( vc2a + vc2b ) + + vc0a < vc1a + vc1a > vc2a + + vc0b > vc1b + vc1b < vc2b + */ + { + BOOST_REQUIRE_EQUAL( early_stats.size(), 2 ); + BOOST_REQUIRE_EQUAL( mid_stats.size(), 2 ); + BOOST_REQUIRE_EQUAL( late_stats.size(), 2 ); + + BOOST_REQUIRE_EQUAL( early_stats[0].value, 8308 ); + BOOST_REQUIRE_EQUAL( early_stats[1].value, 11078 ); + + BOOST_REQUIRE_EQUAL( mid_stats[0].value, 4154 ); + BOOST_REQUIRE_EQUAL( mid_stats[1].value, 9970 ); + + BOOST_REQUIRE_EQUAL( late_stats[0].value, 1384 ); + BOOST_REQUIRE_EQUAL( late_stats[1].value, 2492 ); + + reward_stat::display_stats( early_stats ); + reward_stat::display_stats( mid_stats ); + reward_stat::display_stats( late_stats ); + + auto vc0a = early_stats[ 0 ].value; + auto vc0b = mid_stats[ 0 ].value; + + auto vc1a = early_stats[ 1 ].value; + auto vc1b = late_stats[ 0 ].value; + + auto vc2a = mid_stats[ 1 ].value; + auto vc2b = late_stats[ 1 ].value; + + BOOST_REQUIRE_EQUAL( vc0a + vc0b, vc1a + vc1b ); + BOOST_REQUIRE_EQUAL( vc1a + vc1b, vc2a + vc2b ); + + BOOST_REQUIRE_LT( vc0a, vc1a ); + BOOST_REQUIRE_GT( vc1a, vc2a ); + + BOOST_REQUIRE_GT( vc0b, vc1b ); + BOOST_REQUIRE_LT( vc1b, vc2b ); + } + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_SUITE_END() + + +void two_comments_in_the_same_blocks_impl( cluster_database_fixture& cluster, bool restore_author_reward_curve ) +{ + auto preparation = []( curation_rewards_handler& crh, cluster_database_fixture::ptr_hardfork_database_fixture& executor ) + { + crh.prepare_author( { 0, 1 } ); + crh.prepare_voters_0_80(); + crh.create_printer(); + executor->generate_block(); + + executor->set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + executor->generate_block(); + + crh.prepare_funds(); + executor->generate_block(); + + return crh.calculate_reward(); + }; + + auto execution = []( curation_rewards_handler& crh, cluster_database_fixture::ptr_hardfork_database_fixture& executor, + share_type& total_rewards_pool_before, share_type& total_rewards_pool_after ) + { + size_t vote_counter = 0; + + std::string permlink = "somethingpermlink"; + + crh.prepare_comment( permlink, 0/*author_number*/ ); + crh.prepare_comment( permlink, 1/*author_number*/ ); + executor->generate_block(); + + crh.set_start_time( executor->db->head_block_time() ); + + const uint32_t nr_votes_first = 75; + const uint32_t nr_votes_second = 1; + + //All votes( for both comments ) are in the same block. + for( uint32_t i = 0; i < nr_votes_first; ++i ) + crh.voting( vote_counter, 0/*author_number*/, permlink, { 0 } ); + for( uint32_t i = 0; i < nr_votes_second; ++i ) + crh.voting( vote_counter, 1/*author_number*/, permlink, { 0 } ); + + crh.make_payment( 1/*back_offset_blocks*/ ); + total_rewards_pool_before = crh.current_total_reward(); + crh.make_payment(); + total_rewards_pool_after = crh.current_total_reward(); + }; + + std::vector comment_rewards_hf24; + + auto hf24_content = [&]( cluster_database_fixture::ptr_hardfork_database_fixture& executor ) + { + //=====hf24===== + reward_stat::rewards_stats early_stats[2]; + reward_stat::rewards_stats late_stats[2]; + + share_type total_rewards_pool_before; + share_type total_rewards_pool_after; + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats[0], late_stats[0] ); + + BOOST_REQUIRE_EQUAL( crh.comment_rewards.size(), 2 ); + comment_rewards_hf24 = crh.comment_rewards; + } + { + //Move one element into second collection in order to display in better way. + if( !early_stats[0].empty() ) + { + early_stats[1].emplace_back( early_stats[0].back() ); + early_stats[0].pop_back(); + } + + if( !late_stats[0].empty() ) + { + late_stats[1].emplace_back( late_stats[0].back() ); + late_stats[0].pop_back(); + } + } + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-24*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats[0] ); + reward_stat::display_stats( late_stats[0] ); + + BOOST_TEST_MESSAGE( "curation rewards(2)" ); + reward_stat::display_stats( early_stats[1] ); + reward_stat::display_stats( late_stats[1] ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + + uint32_t _sum_curation_rewards[2] = { 0, 0 }; + { + for( uint32_t c = 0; c < 2; ++c ) + { + for( auto& item : early_stats[c] ) + _sum_curation_rewards[c] += item.value; + } + + BOOST_TEST_MESSAGE( "*****HF24:SUM CURATION REWARDS*****" ); + BOOST_TEST_MESSAGE( "sum_curation_rewards" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards[0] ).c_str() ); + BOOST_TEST_MESSAGE( "sum_curation_rewards(2)" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards[1] ).c_str() ); + + BOOST_REQUIRE_EQUAL( _sum_curation_rewards[0], crh.comment_rewards[0].curation_tokens.value ); + BOOST_REQUIRE_EQUAL( _sum_curation_rewards[1], crh.comment_rewards[1].curation_tokens.value ); + } + { + /* + Not used rewards are returned back after(!) processing all comments, therefore it doesn't matter how much it's returned, + if all comments are in the same block. + */ + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 36951 ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[1].author_tokens.value, 289 ); + } + + executor->validate_database(); + }; + + std::vector comment_rewards_hf25; + + auto hf25_content = [&]( cluster_database_fixture::ptr_hardfork_database_fixture& executor ) + { + //=====hf25===== + reward_stat::rewards_stats early_stats[2]; + reward_stat::rewards_stats mid_stats[2]; + reward_stat::rewards_stats late_stats[2]; + + share_type total_rewards_pool_before; + share_type total_rewards_pool_after; + + if( restore_author_reward_curve ) + { + executor->db_plugin->debug_update( []( database& db ) + { + db.modify( db.get< reward_fund_object, by_name >( HIVE_POST_REWARD_FUND_NAME ), [&]( reward_fund_object& rfo ) + { + rfo.author_reward_curve = convergent_linear; + }); + }); + } + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 25 is activated********************** + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats[0], mid_stats[0], late_stats[0] ); + + BOOST_REQUIRE_EQUAL( crh.comment_rewards.size(), 2 ); + comment_rewards_hf25 = crh.comment_rewards; + } + + { + //Move one element into second collection in order to display in better way. + if( !early_stats[0].empty() ) + { + early_stats[1].emplace_back( early_stats[0].back() ); + early_stats[0].pop_back(); + } + + if( !mid_stats[0].empty() ) + { + mid_stats[1].emplace_back( mid_stats[0].back() ); + mid_stats[0].pop_back(); + } + + if( !late_stats[0].empty() ) + { + late_stats[1].emplace_back( late_stats[0].back() ); + late_stats[0].pop_back(); + } + } + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-25*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats[0] ); + reward_stat::display_stats( mid_stats[0] ); + reward_stat::display_stats( late_stats[0] ); + + BOOST_TEST_MESSAGE( "curation rewards(2)" ); + reward_stat::display_stats( early_stats[1] ); + reward_stat::display_stats( mid_stats[1] ); + reward_stat::display_stats( late_stats[1] ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment(2)" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment(2)" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + + uint32_t _sum_curation_rewards[2] = { 0, 0 }; + { + for( uint32_t c = 0; c < 2; ++c ) + { + for( auto& item : early_stats[c] ) + _sum_curation_rewards[c] += item.value; + } + + BOOST_TEST_MESSAGE( "*****HF25:SUM CURATION REWARDS*****" ); + BOOST_TEST_MESSAGE( "sum_curation_rewards" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards[0] ).c_str() ); + BOOST_TEST_MESSAGE( "sum_curation_rewards(2)" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards[1] ).c_str() ); + + BOOST_REQUIRE_EQUAL( _sum_curation_rewards[0], crh.comment_rewards[0].curation_tokens.value ); + BOOST_REQUIRE_EQUAL( _sum_curation_rewards[1], crh.comment_rewards[1].curation_tokens.value ); + } + { + /* + Not used rewards are returned back after(!) processing all comments, therefore it doesn't matter how much it's returned, + if all comments are in the same block. + */ + if( restore_author_reward_curve ) + { + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 36951 ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[1].author_tokens.value, 289 ); + } + else + { + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 36750 ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[1].author_tokens.value, 490 ); + } + } + + executor->validate_database(); + }; + + cluster.execute_24( hf24_content ); + cluster.execute_25( hf25_content ); + + if( restore_author_reward_curve ) + { + BOOST_REQUIRE_EQUAL( comment_rewards_hf24[0].author_tokens.value, comment_rewards_hf25[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( comment_rewards_hf24[1].author_tokens.value, comment_rewards_hf25[1].author_tokens.value ); + } + else + { + BOOST_REQUIRE_GT( comment_rewards_hf24[0].author_tokens.value, comment_rewards_hf25[0].author_tokens.value ); + BOOST_REQUIRE_LT( comment_rewards_hf24[1].author_tokens.value, comment_rewards_hf25[1].author_tokens.value ); + } +} + +void two_comments_in_different_blocks_impl( cluster_database_fixture& cluster, bool restore_author_reward_curve ) +{ + auto preparation = []( curation_rewards_handler& crh, cluster_database_fixture::ptr_hardfork_database_fixture& executor ) + { + crh.prepare_author( { 0, 1 } ); + crh.prepare_voters_0_80(); + crh.create_printer(); + executor->generate_block(); + + executor->set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + executor->generate_block(); + + crh.prepare_funds(); + executor->generate_block(); + + return crh.calculate_reward(); + }; + + auto execution = []( curation_rewards_handler& crh, cluster_database_fixture::ptr_hardfork_database_fixture& executor, + share_type total_rewards_pool_before[], share_type total_rewards_pool_after[] ) + { + size_t vote_counter = 0; + + const uint32_t nr_votes_first = 75; + const uint32_t nr_votes_second = 1; + + std::string permlink = "somethingpermlink"; + + crh.prepare_comment( permlink, 0/*author_number*/ ); + executor->generate_block(); + crh.set_start_time( executor->db->head_block_time() ); + + //All votes( for both comments ) are in the same block. + for( uint32_t i = 0; i < nr_votes_first; ++i ) + crh.voting( vote_counter, 0/*author_number*/, permlink, { 0 } ); + executor->generate_block(); + + //More blocks in order to generate some rewards by inflation( especially, when HF is applied ) + const int increasing_rewards_factor = 100; + executor->generate_blocks( increasing_rewards_factor ); + + crh.prepare_comment( permlink, 1/*author_number*/ ); + executor->generate_block(); + crh.set_start_time( executor->db->head_block_time(), 1/*comment_idx*/ ); + + //All votes( for both comments ) are in the same block. + for( uint32_t i = 0; i < nr_votes_second; ++i ) + crh.voting( vote_counter, 1/*author_number*/, permlink, { 0 }, 1/*comment_idx*/ ); + executor->generate_block(); + + crh.make_payment( 1/*back_offset_blocks*/ ); + total_rewards_pool_before[0] = crh.current_total_reward(); + crh.make_payment(); + total_rewards_pool_after[0] = crh.current_total_reward(); + + executor->generate_blocks( increasing_rewards_factor ); + + crh.make_payment( 1/*back_offset_blocks*/, 1/*comment_idx*/ ); + total_rewards_pool_before[1] = crh.current_total_reward(); + crh.make_payment( 0/*back_offset_blocks*/, 1/*comment_idx*/ ); + total_rewards_pool_after[1] = crh.current_total_reward(); + }; + + std::vector comment_rewards_hf24; + + auto hf24_content = [&]( cluster_database_fixture::ptr_hardfork_database_fixture& executor ) + { + //=====hf24===== + reward_stat::rewards_stats early_stats[2]; + reward_stat::rewards_stats late_stats[2]; + + share_type total_rewards_pool_before[2]; + share_type total_rewards_pool_after[2]; + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 24 is activated********************** + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats[0], late_stats[0] ); + crh.curation_gathering( early_stats[1], late_stats[1], 1/*comment_idx*/ ); + + BOOST_REQUIRE_EQUAL( crh.comment_rewards.size(), 2 ); + comment_rewards_hf24 = crh.comment_rewards; + } + + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-24*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats[0] ); + reward_stat::display_stats( late_stats[0] ); + + BOOST_TEST_MESSAGE( "curation rewards(2)" ); + reward_stat::display_stats( early_stats[1] ); + reward_stat::display_stats( late_stats[1] ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before[0] ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after[0] ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment(2)" ); + reward_stat::display_reward( total_rewards_pool_before[1] ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment(2)" ); + reward_stat::display_reward( total_rewards_pool_after[1] ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + + uint32_t _sum_curation_rewards[2] = { 0, 0 }; + { + for( uint32_t c = 0; c < 2; ++c ) + { + for( auto& item : early_stats[c] ) + _sum_curation_rewards[c] += item.value; + } + + BOOST_TEST_MESSAGE( "*****HF24:SUM CURATION REWARDS*****" ); + BOOST_TEST_MESSAGE( "sum_curation_rewards" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards[0] ).c_str() ); + BOOST_TEST_MESSAGE( "sum_curation_rewards(2)" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards[1] ).c_str() ); + + BOOST_REQUIRE_EQUAL( _sum_curation_rewards[0], crh.comment_rewards[0].curation_tokens.value ); + BOOST_REQUIRE_EQUAL( _sum_curation_rewards[1], crh.comment_rewards[1].curation_tokens.value ); + } + { + /* + Not used rewards are returned back after processing first comment, + therefore second reward in HF24 is higher than second reward in HF25. + */ + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 40330 ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[1].author_tokens.value, 179 ); + } + + executor->validate_database(); + }; + + std::vector comment_rewards_hf25; + + auto hf25_content = [&]( cluster_database_fixture::ptr_hardfork_database_fixture& executor ) + { + //=====hf25===== + reward_stat::rewards_stats early_stats[2]; + reward_stat::rewards_stats mid_stats[2]; + reward_stat::rewards_stats late_stats[2]; + + share_type total_rewards_pool_before[2]; + share_type total_rewards_pool_after[2]; + + if( restore_author_reward_curve ) + { + executor->db_plugin->debug_update( []( database& db ) + { + db.modify( db.get< reward_fund_object, by_name >( HIVE_POST_REWARD_FUND_NAME ), [&]( reward_fund_object& rfo ) + { + rfo.author_reward_curve = convergent_linear; + }); + }); + } + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 25 is activated********************** + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats[0], mid_stats[0], late_stats[0] ); + crh.curation_gathering( early_stats[1], mid_stats[1], late_stats[1], 1/*comment_idx*/ ); + + BOOST_REQUIRE( !crh.comment_rewards.empty() ); + comment_rewards_hf25 = crh.comment_rewards; + } + + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-25*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats[0] ); + reward_stat::display_stats( mid_stats[0] ); + reward_stat::display_stats( late_stats[0] ); + + BOOST_TEST_MESSAGE( "curation rewards(2)" ); + reward_stat::display_stats( early_stats[1] ); + reward_stat::display_stats( mid_stats[1] ); + reward_stat::display_stats( late_stats[1] ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before[0] ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after[0] ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment(2)" ); + reward_stat::display_reward( total_rewards_pool_before[1] ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment(2)" ); + reward_stat::display_reward( total_rewards_pool_after[1] ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + + uint32_t _sum_curation_rewards[2] = { 0, 0 }; + { + for( uint32_t c = 0; c < 2; ++c ) + { + for( auto& item : early_stats[c] ) + _sum_curation_rewards[c] += item.value; + } + + BOOST_TEST_MESSAGE( "*****HF25:SUM CURATION REWARDS*****" ); + BOOST_TEST_MESSAGE( "sum_curation_rewards" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards[0] ).c_str() ); + BOOST_TEST_MESSAGE( "sum_curation_rewards(2)" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards[1] ).c_str() ); + + BOOST_REQUIRE_EQUAL( _sum_curation_rewards[0], crh.comment_rewards[0].curation_tokens.value ); + BOOST_REQUIRE_EQUAL( _sum_curation_rewards[1], crh.comment_rewards[1].curation_tokens.value ); + } + { + /* + Not used rewards are returned back after processing first comment, + therefore second reward in HF24 is higher than second reward in HF25. + */ + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 40330 ); + if( restore_author_reward_curve ) + { + BOOST_REQUIRE_EQUAL( crh.comment_rewards[1].author_tokens.value, 24 ); + } + else + { + BOOST_REQUIRE_EQUAL( crh.comment_rewards[1].author_tokens.value, 41 ); + } + + } + + executor->validate_database(); + }; + + cluster.execute_24( hf24_content ); + cluster.execute_25( hf25_content ); + + BOOST_REQUIRE_EQUAL( comment_rewards_hf24[0].author_tokens.value, comment_rewards_hf25[0].author_tokens.value ); + BOOST_REQUIRE_GT( comment_rewards_hf24[1].author_tokens.value, comment_rewards_hf25[1].author_tokens.value ); +} + +BOOST_FIXTURE_TEST_SUITE( curation_reward_tests2, cluster_database_fixture ) + +BOOST_AUTO_TEST_CASE( one_vote_per_comment ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards before and after HF25. Reward during whole rewards-time (7 days). Only 1 vote per 1 comment." ); + BOOST_TEST_MESSAGE( "HF24: inside `reverse_auction_seconds` window. HF25: inside `early` window." ); + + auto preparation = []( curation_rewards_handler& crh, ptr_hardfork_database_fixture& executor ) + { + crh.prepare_author( { 0 } ); + crh.prepare_10_voters(); + executor->generate_block(); + + executor->set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + executor->generate_block(); + + crh.prepare_funds(); + executor->generate_block(); + + return crh.calculate_reward(); + }; + + auto execution = []( curation_rewards_handler& crh, ptr_hardfork_database_fixture& executor, + share_type& total_rewards_pool_before, share_type& total_rewards_pool_after ) + { + size_t vote_counter = 0; + uint32_t vote_01_time = 12; + + std::string permlink = "somethingpermlink"; + + crh.prepare_comment( permlink, 0/*author_number*/ ); + executor->generate_block(); + + crh.set_start_time( executor->db->head_block_time() ); + + crh.voting( vote_counter, 0/*author_number*/, permlink, { vote_01_time } ); + crh.make_payment( 1/*back_offset_blocks*/ ); + total_rewards_pool_before = crh.current_total_reward(); + crh.make_payment(); + total_rewards_pool_after = crh.current_total_reward(); + }; + + std::vector comment_rewards_hf24; + + auto hf24_content = [&]( ptr_hardfork_database_fixture& executor ) + { + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats late_stats; + share_type total_rewards_pool_before; + share_type total_rewards_pool_after; + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 24 is activated********************** + //Only 1 vote in `reverse_auction_seconds` window. + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats, late_stats ); + + BOOST_REQUIRE( !crh.comment_rewards.empty() ); + comment_rewards_hf24 = crh.comment_rewards; + } + + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-24*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats ); + reward_stat::display_stats( late_stats ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + { + /* + early_stats.size() == 1 + late_stats == empty + early_stats[0] < author_tokens[0] ( curation_reward < author_reward ) + reward > early_stats[0] + author_tokens[0] ( current_reward > curation_reward + author_reward ) + checking exact values + */ + BOOST_REQUIRE_EQUAL( early_stats.size(), 1 ); + BOOST_REQUIRE( late_stats.empty() ); + BOOST_REQUIRE_LT( early_stats[0].value, crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_GT( crh.comment_rewards[0].total_reward.value, early_stats[0].value + crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( early_stats[0].value, 1864 ); + BOOST_REQUIRE_EQUAL( early_stats[0].value, crh.comment_rewards[0].curation_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 37300 ); + } + { + /* + total_rewards_pool_before + reward_per_block == total_rewards_pool_after + early_stats[0] + author_tokens[0] + */ + auto _temp_before = total_rewards_pool_before.value + reward_per_block.value; + auto _temp_after = total_rewards_pool_after.value + early_stats[0].value + crh.comment_rewards[0].author_tokens.value; + BOOST_REQUIRE_EQUAL( _temp_before, _temp_after ); + } + + executor->validate_database(); + }; + + std::vector comment_rewards_hf25; + + auto hf25_content = [&]( ptr_hardfork_database_fixture& executor ) + { + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats mid_stats; + reward_stat::rewards_stats late_stats; + share_type total_rewards_pool_before; + share_type total_rewards_pool_after; + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 25 is activated********************** + //Only 1 vote in `early` window. + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats, mid_stats, late_stats ); + + BOOST_REQUIRE( !crh.comment_rewards.empty() ); + comment_rewards_hf25 = crh.comment_rewards; + } + + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-25*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats ); + reward_stat::display_stats( mid_stats ); + reward_stat::display_stats( late_stats ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + { + /* + early_stats.size() == 1 + mid_stats == empty + late_stats == empty + early_stats[0] == author_tokens[0] ( curation_reward == author_reward ) + total_reward[0] = early_stats[0] + author_tokens[0] ( current_reward == curation_reward + author_reward ) + checking exact values + */ + BOOST_REQUIRE_EQUAL( early_stats.size(), 1 ); + BOOST_REQUIRE( mid_stats.empty() ); + BOOST_REQUIRE( late_stats.empty() ); + BOOST_REQUIRE_EQUAL( early_stats[0].value, crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].total_reward.value, early_stats[0].value + crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( early_stats[0].value, 37300 ); + BOOST_REQUIRE_EQUAL( early_stats[0].value, crh.comment_rewards[0].curation_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 37300 ); + } + { + /* + total_rewards_pool_before + reward_per_block == total_rewards_pool_after + early_stats[0] + author_tokens[0] + */ + auto _temp_before = total_rewards_pool_before.value + reward_per_block.value; + auto _temp_after = total_rewards_pool_after.value + early_stats[0].value + crh.comment_rewards[0].author_tokens.value; + BOOST_REQUIRE_EQUAL( _temp_before, _temp_after ); + } + + executor->validate_database(); + }; + + execute_24( hf24_content ); + execute_25( hf25_content ); + + BOOST_REQUIRE_EQUAL( comment_rewards_hf24[0].author_tokens.value, comment_rewards_hf25[0].author_tokens.value ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( one_vote_per_comment_v2 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards before and after HF25. Reward during whole rewards-time (7 days). Only 1 vote per 1 comment." ); + BOOST_TEST_MESSAGE( "HF24: exactly when `reverse_auction_seconds` window finishes. HF25: inside `early` window." ); + + auto preparation = []( curation_rewards_handler& crh, ptr_hardfork_database_fixture& executor ) + { + crh.prepare_author( { 0 } ); + crh.prepare_10_voters(); + executor->generate_block(); + + executor->set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + executor->generate_block(); + + crh.prepare_funds(); + executor->generate_block(); + + return crh.calculate_reward(); + }; + + auto execution = []( curation_rewards_handler& crh, ptr_hardfork_database_fixture& executor, + share_type& total_rewards_pool_before, share_type& total_rewards_pool_after ) + { + size_t vote_counter = 0; + uint32_t vote_01_time = 300/*old value of 'reverse_auction_seconds'*/; + + std::string permlink = "somethingpermlink"; + + crh.prepare_comment( permlink, 0/*author_number*/ ); + executor->generate_block(); + + crh.set_start_time( executor->db->head_block_time() ); + + crh.voting( vote_counter, 0/*author_number*/, permlink, { vote_01_time } ); + crh.make_payment( 1/*back_offset_blocks*/ ); + total_rewards_pool_before = crh.current_total_reward(); + crh.make_payment(); + total_rewards_pool_after = crh.current_total_reward(); + }; + + std::vector comment_rewards_hf24; + + auto hf24_content = [&]( ptr_hardfork_database_fixture& executor ) + { + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats late_stats; + share_type total_rewards_pool_before; + share_type total_rewards_pool_after; + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 24 is activated********************** + //Only 1 vote in `reverse_auction_seconds` window. + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats, late_stats ); + BOOST_REQUIRE( !crh.comment_rewards.empty() ); + comment_rewards_hf24 = crh.comment_rewards; + } + + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-24*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats ); + reward_stat::display_stats( late_stats ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + { + /* + early_stats.size() == empty + late_stats == 1 + late_stats[0] == author_tokens[0] ( curation_reward == author_reward ) + crh.comment_rewards[0].total_reward.value == late_stats[0] + author_tokens[0] ( current_reward == curation_reward + author_reward ) + checking exact values + */ + BOOST_REQUIRE( early_stats.empty() ); + BOOST_REQUIRE_EQUAL( late_stats.size(), 1 ); + BOOST_REQUIRE_EQUAL( late_stats[0].value, crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].total_reward.value, late_stats[0].value + crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( late_stats[0].value, 37300 ); + BOOST_REQUIRE_EQUAL( late_stats[0].value, crh.comment_rewards[0].curation_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 37300 ); + } + { + /* + total_rewards_pool_before + reward_per_block == total_rewards_pool_after + late_stats[0] + author_tokens[0] + */ + auto _temp_before = total_rewards_pool_before.value + reward_per_block.value; + auto _temp_after = total_rewards_pool_after.value + late_stats[0].value + crh.comment_rewards[0].author_tokens.value; + BOOST_REQUIRE_EQUAL( _temp_before, _temp_after ); + } + + executor->validate_database(); + }; + + std::vector comment_rewards_hf25; + + auto hf25_content = [&]( ptr_hardfork_database_fixture& executor ) + { + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats mid_stats; + reward_stat::rewards_stats late_stats; + share_type total_rewards_pool_before; + share_type total_rewards_pool_after; + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 25 is activated********************** + //Only 1 vote in `early` window. + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats, mid_stats, late_stats ); + BOOST_REQUIRE( !crh.comment_rewards.empty() ); + comment_rewards_hf25 = crh.comment_rewards; + } + + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-25*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats ); + reward_stat::display_stats( mid_stats ); + reward_stat::display_stats( late_stats ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + { + /* + early_stats.size() == 1 + mid_stats == empty + late_stats == empty + early_stats[0] == author_tokens[0] ( curation_reward == author_reward ) + crh.comment_rewards[0].total_reward.value = early_stats[0] + author_tokens[0] ( current_reward == curation_reward + author_reward ) + checking exact values + */ + BOOST_REQUIRE_EQUAL( early_stats.size(), 1 ); + BOOST_REQUIRE( mid_stats.empty() ); + BOOST_REQUIRE( late_stats.empty() ); + BOOST_REQUIRE_EQUAL( early_stats[0].value, crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].total_reward.value, early_stats[0].value + crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( early_stats[0].value, 37300 ); + BOOST_REQUIRE_EQUAL( early_stats[0].value, crh.comment_rewards[0].curation_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 37300 ); + } + { + /* + total_rewards_pool_before + reward_per_block == total_rewards_pool_after + early_stats[0] + author_tokens[0] + */ + auto _temp_before = total_rewards_pool_before.value + reward_per_block.value; + auto _temp_after = total_rewards_pool_after.value + early_stats[0].value + crh.comment_rewards[0].author_tokens.value; + BOOST_REQUIRE_EQUAL( _temp_before, _temp_after ); + } + + executor->validate_database(); + }; + + execute_24( hf24_content ); + execute_25( hf25_content ); + + BOOST_REQUIRE_EQUAL( comment_rewards_hf24[0].author_tokens.value, comment_rewards_hf25[0].author_tokens.value ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( five_votes_per_comment ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards before and after HF25. Reward during whole rewards-time (7 days). 5 votes per 1 comment." ); + BOOST_TEST_MESSAGE( "HF24: after `reverse_auction_seconds` window. HF25: inside `early` window." ); + + auto preparation = []( curation_rewards_handler& crh, ptr_hardfork_database_fixture& executor ) + { + crh.prepare_author( { 0 } ); + crh.prepare_10_voters(); + executor->generate_block(); + + executor->set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + executor->generate_block(); + + crh.prepare_funds(); + executor->generate_block(); + + return crh.calculate_reward(); + }; + + auto execution = []( curation_rewards_handler& crh, ptr_hardfork_database_fixture& executor, + share_type& total_rewards_pool_before, share_type& total_rewards_pool_after ) + { + size_t vote_counter = 0; + uint32_t vote_01_time = 300/*old value of 'reverse_auction_seconds'*/ + 3; + + std::string permlink = "somethingpermlink"; + + crh.prepare_comment( permlink, 0/*author_number*/ ); + executor->generate_block(); + + crh.set_start_time( executor->db->head_block_time() ); + + crh.voting( vote_counter, 0/*author_number*/, permlink, { vote_01_time } ); + crh.voting( vote_counter, 0/*author_number*/, permlink, { 0 } ); + crh.voting( vote_counter, 0/*author_number*/, permlink, { 0 } ); + crh.voting( vote_counter, 0/*author_number*/, permlink, { 0 } ); + crh.voting( vote_counter, 0/*author_number*/, permlink, { 0 } ); + crh.make_payment( 1/*back_offset_blocks*/ ); + total_rewards_pool_before = crh.current_total_reward(); + crh.make_payment(); + total_rewards_pool_after = crh.current_total_reward(); + }; + + std::vector comment_rewards_hf24; + + auto hf24_content = [&]( ptr_hardfork_database_fixture& executor ) + { + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats late_stats; + share_type total_rewards_pool_before; + share_type total_rewards_pool_after; + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 24 is activated********************** + //5 votes in `reverse_auction_seconds` window. + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats, late_stats ); + BOOST_REQUIRE( !crh.comment_rewards.empty() ); + comment_rewards_hf24 = crh.comment_rewards; + } + + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-24*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats ); + reward_stat::display_stats( late_stats ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + + const uint32_t nr_votes = 5; + uint32_t _sum_curation_rewards = 0; + { + /* + early_stats.size() == empty + late_stats == nr_votes + previous_reward > next_reward + _sum_curation_rewards == author_tokens[0] ( sum_curation_rewards == author_reward ) + crh.comment_rewards[0].total_reward.value == _sum_curation_rewards + author_tokens[0] ( current_reward == sum_curation_rewards + author_reward ) + checking exact values + */ + BOOST_REQUIRE( early_stats.empty() ); + BOOST_REQUIRE_EQUAL( late_stats.size(), nr_votes ); + + //calculating of auxiliary sum + for( uint32_t i = 0; i < nr_votes; ++i ) + _sum_curation_rewards += late_stats[i].value; + + BOOST_TEST_MESSAGE( "sum_curation_rewards" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards ).c_str() ); + + BOOST_REQUIRE_EQUAL( _sum_curation_rewards, crh.comment_rewards[0].curation_tokens.value ); + + for( uint32_t i = 0; i < nr_votes - 1; ++i ) + BOOST_REQUIRE_GT( late_stats[i].value, late_stats[i+1].value ); + + BOOST_REQUIRE_EQUAL( _sum_curation_rewards + 2/*rounding*/, crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].total_reward.value, _sum_curation_rewards + 2/*rounding*/ + crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( late_stats[0].value, 10529 ); + BOOST_REQUIRE_EQUAL( late_stats[1].value, 8551 ); + BOOST_REQUIRE_EQUAL( late_stats[2].value, 7083 ); + BOOST_REQUIRE_EQUAL( late_stats[3].value, 5963 ); + BOOST_REQUIRE_EQUAL( late_stats[4].value, 5172 ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 37300 ); + } + { + /* + total_rewards_pool_before + reward_per_block == total_rewards_pool_after + _sum + author_tokens[0] + */ + auto _temp_before = total_rewards_pool_before.value + reward_per_block.value; + auto _temp_after = total_rewards_pool_after.value + _sum_curation_rewards + crh.comment_rewards[0].author_tokens.value; + BOOST_REQUIRE_EQUAL( _temp_before, _temp_after ); + } + + executor->validate_database(); + }; + + std::vector comment_rewards_hf25; + + auto hf25_content = [&]( ptr_hardfork_database_fixture& executor ) + { + reward_stat::rewards_stats early_stats; + reward_stat::rewards_stats mid_stats; + reward_stat::rewards_stats late_stats; + share_type total_rewards_pool_before; + share_type total_rewards_pool_after; + + curation_rewards_handler crh( *executor, *( executor->db ) ); + + share_type reward_per_block = preparation( crh, executor ); + + { + //**********************HF 25 is activated********************** + //5 votes in `reverse_auction_seconds` window. + execution( crh, executor, total_rewards_pool_before, total_rewards_pool_after ); + + crh.curation_gathering( early_stats, mid_stats, late_stats ); + BOOST_REQUIRE( !crh.comment_rewards.empty() ); + comment_rewards_hf25 = crh.comment_rewards; + } + + { + BOOST_TEST_MESSAGE( "reward_per_block" ); + reward_stat::display_reward( reward_per_block ); + + BOOST_TEST_MESSAGE( "*****HF-25*****" ); + BOOST_TEST_MESSAGE( "curation rewards" ); + reward_stat::display_stats( early_stats ); + reward_stat::display_stats( mid_stats ); + reward_stat::display_stats( late_stats ); + + BOOST_TEST_MESSAGE( "current pool of rewards before payment" ); + reward_stat::display_reward( total_rewards_pool_before ); + BOOST_TEST_MESSAGE( "current pool of rewards after payment" ); + reward_stat::display_reward( total_rewards_pool_after ); + + BOOST_TEST_MESSAGE( "comment rewards" ); + for( auto& item : crh.comment_rewards ) + reward_stat::display_comment_rewards( item ); + } + + const uint32_t nr_votes = 5; + uint32_t _sum_curation_rewards = 0; + { + /* + early_stats.size() == nr_votes + mid_stats == empty + late_stats == empty + previous_reward == next_reward + _sum_curation_rewards == author_tokens[0] ( sum_curation_rewards == author_reward ) + crh.comment_rewards[0].total_reward.value = _sum_curation_rewards + author_tokens[0] ( current_reward == sum_curation_rewards + author_reward ) + checking exact values + */ + BOOST_REQUIRE_EQUAL( early_stats.size(), nr_votes ); + BOOST_REQUIRE( mid_stats.empty() ); + BOOST_REQUIRE( late_stats.empty() ); + + //calculating of auxiliary sum + for( uint32_t i = 0; i < nr_votes; ++i ) + _sum_curation_rewards += early_stats[i].value; + + BOOST_TEST_MESSAGE( "sum_curation_rewards" ); + BOOST_TEST_MESSAGE( std::to_string( _sum_curation_rewards ).c_str() ); + + BOOST_REQUIRE_EQUAL( _sum_curation_rewards, crh.comment_rewards[0].curation_tokens.value ); + + for( uint32_t i = 0; i < nr_votes - 1; ++i ) + BOOST_REQUIRE_EQUAL( early_stats[i].value, early_stats[i+1].value ); + + BOOST_REQUIRE_EQUAL( _sum_curation_rewards, crh.comment_rewards[0].author_tokens.value ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].total_reward.value, _sum_curation_rewards + crh.comment_rewards[0].author_tokens.value ); + + for( uint32_t i = 0; i < nr_votes; ++i ) + BOOST_REQUIRE_EQUAL( early_stats[i].value, 7460 ); + BOOST_REQUIRE_EQUAL( crh.comment_rewards[0].author_tokens.value, 37300 ); + } + { + /* + total_rewards_pool_before + reward_per_block == total_rewards_pool_after + _sum + author_tokens[0] + */ + auto _temp_before = total_rewards_pool_before.value + reward_per_block.value; + auto _temp_after = total_rewards_pool_after.value + _sum_curation_rewards + crh.comment_rewards[0].author_tokens.value; + BOOST_REQUIRE_EQUAL( _temp_before, _temp_after ); + } + + executor->validate_database(); + }; + + execute_24( hf24_content ); + execute_25( hf25_content ); + + BOOST_REQUIRE_EQUAL( comment_rewards_hf24[0].author_tokens.value, comment_rewards_hf25[0].author_tokens.value ); + + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( two_comments_in_the_same_blocks ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards before and after HF25. Reward during whole rewards-time (7 days). 2 comments in every hardfork." ); + BOOST_TEST_MESSAGE( "HF24: Both comments in `reverse_auction_seconds` window in the same block. First comment with many votes, second comment with 1 vote." ); + BOOST_TEST_MESSAGE( "HF25: Both comments in `early_window` in the same block. First comment with many votes, second comment with 1 vote." ); + + /* + Changes in HF25: + 1) curation_reward_curve: convergent_square_root -> linear + 2) author_reward_curve: convergent_linear -> linear + + This test checks only the change number `1`, so it's possible to check how this change influences on author rewards, + taking into consideration HF24 vs HF25. + */ + two_comments_in_the_same_blocks_impl( *this, true/*restore_author_reward_curve*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( two_comments_in_the_same_blocks_v2 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards before and after HF25. Reward during whole rewards-time (7 days). 2 comments in every hardfork." ); + BOOST_TEST_MESSAGE( "HF24: Both comments in `reverse_auction_seconds` window in the same block. First comment with many votes, second comment with 1 vote." ); + BOOST_TEST_MESSAGE( "HF25: Both comments in `early_window` in the same block. First comment with many votes, second comment with 1 vote." ); + + two_comments_in_the_same_blocks_impl( *this, false/*restore_author_reward_curve*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( two_comments_in_different_blocks ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards before and after HF25. Reward during whole rewards-time (7 days). 2 comments in every hardfork." ); + BOOST_TEST_MESSAGE( "HF24: Both comments in `reverse_auction_seconds` window in different blocks. First comment with many votes, second comment with 1 vote." ); + BOOST_TEST_MESSAGE( "HF25: Both comments in `early_window` in different blocks. First comment with many votes, second comment with 1 vote." ); + + /* + Changes in HF25: + 1) curation_reward_curve: convergent_square_root -> linear + 2) author_reward_curve: convergent_linear -> linear + + This test checks only the change number `1`, so it's possible to check how this change influences on author rewards, + taking into consideration HF24 vs HF25. + */ + two_comments_in_different_blocks_impl( *this, true/*restore_author_reward_curve*/ ); + + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( two_comments_in_different_blocks_v2 ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: curation rewards before and after HF25. Reward during whole rewards-time (7 days). 2 comments in every hardfork." ); + BOOST_TEST_MESSAGE( "HF24: Both comments in `reverse_auction_seconds` window in different blocks. First comment with many votes, second comment with 1 vote." ); + BOOST_TEST_MESSAGE( "HF25: Both comments in `early_window` in different blocks. First comment with many votes, second comment with 1 vote." ); + + two_comments_in_different_blocks_impl( *this, false/*restore_author_reward_curve*/ ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit/tests/delayed_voting_tests.cpp b/tests/unit/tests/delayed_voting_tests.cpp index 76a610d231..c692827caf 100644 --- a/tests/unit/tests/delayed_voting_tests.cpp +++ b/tests/unit/tests/delayed_voting_tests.cpp @@ -38,7 +38,7 @@ using namespace hive::chain; using namespace hive::protocol; using fc::string; -constexpr int DAYS_FOR_DELAYED_VOTING{ (HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS / HIVE_DELAYED_VOTING_INTERVAL_SECONDS) }; +constexpr int NR_INTERVALS_IN_DELAYED_VOTING{ (HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS / HIVE_DELAYED_VOTING_INTERVAL_SECONDS) }; #define VOTING_POWER( account ) db->get_account( account ).witness_vote_weight().value #define PROXIED_VSF( account ) db->get_account( account ).proxied_vsf_votes_total().value @@ -92,8 +92,8 @@ BOOST_AUTO_TEST_CASE( delayed_proposal_test_01 ) // create one proposal create_proposal_data cpd(db->head_block_time()); - cpd.end_date = cpd.start_date + fc::days( 2* DAYS_FOR_DELAYED_VOTING ); - int64_t proposal_1 = create_proposal( cpd.creator, cpd.receiver, cpd.start_date, cpd.end_date, cpd.daily_pay, alice_private_key ); + cpd.end_date = cpd.start_date + fc::days( 2* NR_INTERVALS_IN_DELAYED_VOTING ); + int64_t proposal_1 = create_proposal( cpd.creator, cpd.receiver, cpd.start_date, cpd.end_date, cpd.daily_pay, alice_private_key, false/*with_block_generation*/ ); BOOST_REQUIRE(proposal_1 >= 0); // carol vest @@ -102,19 +102,23 @@ BOOST_AUTO_TEST_CASE( delayed_proposal_test_01 ) // carol votes vote_proposal("carol", { proposal_1 }, true, carol_private_key); + const int cycle = 24; + const int time_offset = HIVE_DELAYED_VOTING_INTERVAL_SECONDS / cycle; // check - for(int i = 0; i < (24*DAYS_FOR_DELAYED_VOTING) - 1; i++) + for(int i = 0; i < (cycle*NR_INTERVALS_IN_DELAYED_VOTING) - 1; i++) { - generate_blocks( db->head_block_time() + fc::hours(1).to_seconds()); + generate_blocks( db->head_block_time() + fc::seconds( time_offset ).to_seconds()); auto * ptr = find_proposal(proposal_1); BOOST_REQUIRE( ptr != nullptr ); BOOST_REQUIRE( ptr->total_votes == 0ul ); } - generate_days_blocks( 1, true ); + generate_seconds_blocks( time_offset, true ); + generate_seconds_blocks( 3600, true ); + auto * ptr = find_proposal(proposal_1); BOOST_REQUIRE( ptr != nullptr ); - BOOST_REQUIRE( static_cast( ptr->total_votes ) == get_vesting( "carol" ).amount.value ); + BOOST_REQUIRE_EQUAL( static_cast( ptr->total_votes ), get_vesting( "carol" ).amount.value ); validate_database(); } @@ -157,14 +161,16 @@ BOOST_AUTO_TEST_CASE( delayed_proposal_test_02 ) // create one proposal create_proposal_data cpd1(db->head_block_time()); - cpd1.end_date = cpd1.start_date + fc::days( 2* DAYS_FOR_DELAYED_VOTING ); - int64_t proposal_1 = create_proposal( cpd1.creator, cpd1.receiver, cpd1.start_date, cpd1.end_date, cpd1.daily_pay, alice_private_key ); + cpd1.end_date = cpd1.start_date + fc::days( 2* NR_INTERVALS_IN_DELAYED_VOTING ); + cpd1.creator = "alice"; + int64_t proposal_1 = create_proposal( cpd1.creator, cpd1.receiver, cpd1.start_date, cpd1.end_date, cpd1.daily_pay, alice_private_key, false/*with_block_generation*/ ); BOOST_REQUIRE(proposal_1 >= 0); // create one proposal create_proposal_data cpd2(db->head_block_time()); - cpd2.end_date = cpd2.start_date + fc::days( 2* DAYS_FOR_DELAYED_VOTING ); - int64_t proposal_2 = create_proposal( cpd2.creator, cpd2.receiver, cpd2.start_date, cpd2.end_date, cpd2.daily_pay, alice_private_key ); + cpd2.end_date = cpd2.start_date + fc::days( 2* NR_INTERVALS_IN_DELAYED_VOTING ); + cpd2.creator = "bob"; + int64_t proposal_2 = create_proposal( cpd2.creator, cpd2.receiver, cpd2.start_date, cpd2.end_date, cpd2.daily_pay, bob_private_key, false/*with_block_generation*/ ); BOOST_REQUIRE(proposal_2 >= 0); // carol vest @@ -177,20 +183,22 @@ BOOST_AUTO_TEST_CASE( delayed_proposal_test_02 ) auto * ptr = find_proposal(proposal_1); //<- just init // check - for(int i = 0; i < (24*DAYS_FOR_DELAYED_VOTING) - 1; i++) + const int cycle = 24; + const int time_offset = HIVE_DELAYED_VOTING_INTERVAL_SECONDS / cycle; + for(int i = 0; i < (cycle*NR_INTERVALS_IN_DELAYED_VOTING) - 1; i++) { - generate_blocks( db->head_block_time() + fc::hours(1).to_seconds()); + generate_blocks( db->head_block_time() + fc::seconds(time_offset).to_seconds()); ptr = find_proposal(proposal_1); BOOST_REQUIRE( ptr != nullptr ); BOOST_REQUIRE( ptr->total_votes == 0ul ); - if( i == (12 * DAYS_FOR_DELAYED_VOTING)) + if( i == (12 * NR_INTERVALS_IN_DELAYED_VOTING)) { vest("carol", "carol", ASSET("10.000 TESTS"), carol_private_key); vote_proposal("carol", { proposal_1, proposal_2 }, true, carol_private_key); } - if(i > (12 * DAYS_FOR_DELAYED_VOTING)) + if(i > (12 * NR_INTERVALS_IN_DELAYED_VOTING)) { ptr = find_proposal(proposal_2); BOOST_REQUIRE( ptr != nullptr ); @@ -198,7 +206,8 @@ BOOST_AUTO_TEST_CASE( delayed_proposal_test_02 ) } } - generate_days_blocks( 1, true ); + generate_seconds_blocks( time_offset, true ); + generate_seconds_blocks( 3600, true ); ptr = find_proposal(proposal_1); BOOST_REQUIRE( ptr != nullptr ); BOOST_REQUIRE( static_cast(ptr->total_votes) == carol_power_1 ); @@ -207,9 +216,9 @@ BOOST_AUTO_TEST_CASE( delayed_proposal_test_02 ) BOOST_REQUIRE( ptr != nullptr ); BOOST_REQUIRE( static_cast(ptr->total_votes) == carol_power_1 ); - for(int i = 0; i < ( 12 * ( DAYS_FOR_DELAYED_VOTING / 2 ) ) - 1; i++) + for(int i = 0; i < ( 12 * ( NR_INTERVALS_IN_DELAYED_VOTING / 2 ) ) - 1; i++) { - generate_blocks( db->head_block_time() + fc::hours(1).to_seconds()); + generate_blocks( db->head_block_time() + fc::seconds(time_offset).to_seconds()); ptr = find_proposal(proposal_2); BOOST_REQUIRE( ptr != nullptr ); BOOST_REQUIRE( static_cast(ptr->total_votes) == carol_power_1 ); @@ -217,7 +226,7 @@ BOOST_AUTO_TEST_CASE( delayed_proposal_test_02 ) for(int _ = 0; _ < 8; _++ ) { - generate_days_blocks( 1, true ); + generate_seconds_blocks( 3600, true ); ptr = find_proposal(proposal_2); } BOOST_REQUIRE( get_vesting( "carol" ).amount.value == static_cast(ptr->total_votes) ); @@ -238,6 +247,9 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) { BOOST_TEST_MESSAGE( "Testing: Setting proxy - more complex tests" ); + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + auto start_time = db->head_block_time(); ACTORS( (alice)(bob)(celine)(witness1)(witness2) ) @@ -268,11 +280,11 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) proxy_data proxy_31; - auto fill = [ this ]( proxy_data& proxy, const std::string& account_name, size_t day, size_t minutes ) + auto fill = [ this ]( proxy_data& proxy, const std::string& account_name, size_t nr_interval, size_t seconds ) { auto dq = db->get_account( account_name ).delayed_votes; - fc::optional< size_t > idx = get_position_in_delayed_voting_array( dq, day, minutes ); + fc::optional< size_t > idx = get_position_in_delayed_voting_array( dq, nr_interval, seconds ); if( !idx.valid() ) return; @@ -284,18 +296,18 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) proxy[ account_name ] = dq[ *idx ]; }; - auto cmp = [ this ]( proxy_data& proxy, const std::string& account_name, size_t val, uint32_t day, size_t minutes ) + auto cmp = [ this ]( proxy_data& proxy, const std::string& account_name, size_t val, size_t nr_interval, size_t seconds ) { auto dq = db->get_account( account_name ).delayed_votes; - fc::optional< size_t > idx = get_position_in_delayed_voting_array( dq, day, minutes ); + fc::optional< size_t > idx = get_position_in_delayed_voting_array( dq, nr_interval, seconds ); if( !idx.valid() ) return true; auto found = proxy.find( account_name ); if( found == proxy.end() ) { - //current day doesn't exist yet, but previous day must exist + //current number of interval doesn't exist yet, but previous number of interval must exist BOOST_REQUIRE( dq.size() > 0 ); return true; } @@ -309,22 +321,20 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) share_type votes_witness1 = get_votes( "witness1" ); share_type votes_witness2 = get_votes( "witness2" ); - size_t day = 0; + size_t nr_interval = 0; + { BOOST_TEST_MESSAGE( "Getting init data..." ); - fill( proxy_0, "alice", day, 0/*minutes*/ ); - fill( proxy_0, "bob", day, 0/*minutes*/ ); - fill( proxy_0, "celine", day, 0/*minutes*/ ); - fill( proxy_0, "witness1", day, 0/*minutes*/ ); - fill( proxy_0, "witness2", day, 0/*minutes*/ ); + fill( proxy_0, "alice", nr_interval, 0/*seconds*/ ); + fill( proxy_0, "bob", nr_interval, 0/*seconds*/ ); + fill( proxy_0, "celine", nr_interval, 0/*seconds*/ ); + fill( proxy_0, "witness1", nr_interval, 0/*seconds*/ ); + fill( proxy_0, "witness2", nr_interval, 0/*seconds*/ ); } { BOOST_TEST_MESSAGE( "Preparing accounts..." ); - set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); - generate_block(); - FUND( "alice", ASSET( "10000.000 TESTS" ) ); FUND( "bob", ASSET( "10000.000 TESTS" ) ); FUND( "celine", ASSET( "10000.000 TESTS" ) ); @@ -337,32 +347,32 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) auto _v1 = vamount( _1 ); vest( "alice", "alice", _1, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_0, "alice", _v1, day, 0/*minutes*/ ) ); - fill( proxy_0, "alice", day, 0/*minutes*/ ); + BOOST_REQUIRE( cmp( proxy_0, "alice", _v1, nr_interval, 0/*seconds*/ ) ); + fill( proxy_0, "alice", nr_interval, 0/*seconds*/ ); generate_block(); auto _v2 = vamount( _2 ); vest( "alice", "bob", _2, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_0, "bob", _v2, day, 0/*minutes*/ ) ); - fill( proxy_0, "bob", day, 0/*minutes*/ ); + BOOST_REQUIRE( cmp( proxy_0, "bob", _v2, nr_interval, 0/*seconds*/ ) ); + fill( proxy_0, "bob", nr_interval, 0/*seconds*/ ); generate_block(); auto _v3 = vamount( _3 ); vest( "alice", "celine", _3, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_0, "celine", _v3, day, 0/*minutes*/ ) ); - fill( proxy_0, "celine", day, 0/*minutes*/ ); + BOOST_REQUIRE( cmp( proxy_0, "celine", _v3, nr_interval, 0/*seconds*/ ) ); + fill( proxy_0, "celine", nr_interval, 0/*seconds*/ ); generate_block(); auto _v4 = vamount( _4 ); vest( "alice", "witness1", _4, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_0, "witness1", _v4, day, 0/*minutes*/ ) ); - fill( proxy_0, "witness1", day, 0/*minutes*/ ); + BOOST_REQUIRE( cmp( proxy_0, "witness1", _v4, nr_interval, 0/*seconds*/ ) ); + fill( proxy_0, "witness1", nr_interval, 0/*seconds*/ ); generate_block(); auto _v5 = vamount( _5 ); vest( "alice", "witness2", _5, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_0, "witness2", _v5, day, 0/*minutes*/ ) ); - fill( proxy_0, "witness2", day, 0/*minutes*/ ); + BOOST_REQUIRE( cmp( proxy_0, "witness2", _v5, nr_interval, 0/*seconds*/ ) ); + fill( proxy_0, "witness2", nr_interval, 0/*seconds*/ ); generate_block(); BOOST_REQUIRE( get_votes( "witness1" ) == votes_witness1 ); @@ -409,11 +419,11 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) */ } - size_t minutes = 0; + size_t seconds = 0; { /* - *****`+ 1 day`***** + *****`+ 1 nr_interval`***** `alice` makes vests `bob` makes vests `alice` makes vests @@ -422,46 +432,46 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) */ start_time += HIVE_DELAYED_VOTING_INTERVAL_SECONDS; generate_blocks( start_time, true ); - day = 1; + nr_interval = 1; - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*1*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*1*/ ), true ); auto _v1 = vamount( _1 ); vest( "alice", "alice", _1, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_1, "alice", _v1, day, minutes ) ); - fill( proxy_1, "alice", day, minutes ); + BOOST_REQUIRE( cmp( proxy_1, "alice", _v1, nr_interval, seconds ) ); + fill( proxy_1, "alice", nr_interval, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*2*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*2*/ ), true ); auto _v2 = vamount( _2 ); vest( "alice", "bob", _2, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_1, "bob", _v2, day, minutes ) ); - fill( proxy_1, "bob", day, minutes ); + BOOST_REQUIRE( cmp( proxy_1, "bob", _v2, nr_interval, seconds ) ); + fill( proxy_1, "bob", nr_interval, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*3*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*3*/ ), true ); _v1 = vamount( _1 ); vest( "alice", "alice", _1, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_1, "alice", _v1, day, minutes ) ); - fill( proxy_1, "alice", day, minutes ); + BOOST_REQUIRE( cmp( proxy_1, "alice", _v1, nr_interval, seconds ) ); + fill( proxy_1, "alice", nr_interval, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*4*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*4*/ ), true ); _v2 = vamount( _2 ); vest( "alice", "bob", _2, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_1, "bob", _v2, day, minutes ) ); - fill( proxy_1, "bob", day, minutes ); + BOOST_REQUIRE( cmp( proxy_1, "bob", _v2, nr_interval, seconds ) ); + fill( proxy_1, "bob", nr_interval, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*5*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*5*/ ), true ); _v1 = vamount( _1 ); vest( "alice", "celine", _1, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_1, "celine", _v1, day, minutes ) ); - fill( proxy_1, "celine", day, minutes ); + BOOST_REQUIRE( cmp( proxy_1, "celine", _v1, nr_interval, seconds ) ); + fill( proxy_1, "celine", nr_interval, seconds ); generate_block(); BOOST_REQUIRE( get_votes( "witness1" ) == votes_witness1 ); @@ -469,28 +479,28 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) } { /* - *****`+ 2 days`***** + *****`+ 2 nr_intervals`***** `alice` makes vests `celine` makes vests */ start_time += HIVE_DELAYED_VOTING_INTERVAL_SECONDS; generate_blocks( start_time, true ); - day = 2; + nr_interval = 2; - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*6*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*6*/ ), true ); auto _v5 = vamount( _5 ); vest( "alice", "alice", _5, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_2, "alice", _v5, day, minutes ) ); - fill( proxy_2, "alice", day, minutes ); + BOOST_REQUIRE( cmp( proxy_2, "alice", _v5, nr_interval, seconds ) ); + fill( proxy_2, "alice", nr_interval, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*7*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*7*/ ), true ); auto _v2 = vamount( _2 ); vest( "alice", "celine", _2, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_2, "celine", _v2, day, minutes ) ); - fill( proxy_2, "celine", day, minutes ); + BOOST_REQUIRE( cmp( proxy_2, "celine", _v2, nr_interval, seconds ) ); + fill( proxy_2, "celine", nr_interval, seconds ); generate_block(); BOOST_REQUIRE( get_votes( "witness1" ) == votes_witness1 ); @@ -498,37 +508,37 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) } { /* - *****`+ 15 days`***** + *****`+ 15 nr_intervals`***** `alice` makes vests `bob` makes vests `celine` makes vests */ start_time += 13 * HIVE_DELAYED_VOTING_INTERVAL_SECONDS; generate_blocks( start_time, true ); - day = 15; + nr_interval = 15; - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*8*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*8*/ ), true ); auto _v2 = vamount( _2 ); vest( "alice", "alice", _2, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_15, "alice", _v2, day, minutes ) ); - fill( proxy_15, "alice", day, minutes ); + BOOST_REQUIRE( cmp( proxy_15, "alice", _v2, nr_interval, seconds ) ); + fill( proxy_15, "alice", nr_interval, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*9*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*9*/ ), true ); auto _v3 = vamount( _3 ); vest( "alice", "bob", _3, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_15, "bob", _v3, day, minutes ) ); - fill( proxy_15, "bob", day, minutes ); + BOOST_REQUIRE( cmp( proxy_15, "bob", _v3, nr_interval, seconds ) ); + fill( proxy_15, "bob", nr_interval, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*10*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*10*/ ), true ); auto _v4 = vamount( _4 ); vest( "alice", "celine", _4, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_15, "celine", _v4, day, minutes ) ); - fill( proxy_15, "celine", day, minutes ); + BOOST_REQUIRE( cmp( proxy_15, "celine", _v4, nr_interval, seconds ) ); + fill( proxy_15, "celine", nr_interval, seconds ); generate_block(); BOOST_REQUIRE( get_votes( "witness1" ) == votes_witness1 ); @@ -536,7 +546,7 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) } { /* - *****`30 days - 1 block`***** + *****`30 nr_intervals - 1 block`***** */ start_time += 15 * HIVE_DELAYED_VOTING_INTERVAL_SECONDS - HIVE_BLOCK_INTERVAL; generate_blocks( start_time, true ); @@ -600,14 +610,14 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) { /* - *****`30 days`***** + *****`30 nr_intervals`***** `alice` makes vests `bob` makes vests `celine` makes vests */ start_time += HIVE_BLOCK_INTERVAL; generate_blocks( start_time, true ); - day = 30; + nr_interval = 30; size_t diff = 1;//because `1` element in delayed_votes has been removed already BOOST_REQUIRE( witness1_result_00 >= 0 ); @@ -616,28 +626,28 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) BOOST_REQUIRE( get_votes( "witness1" ) == ( votes_witness1 + witness1_result_00.value ) ); BOOST_REQUIRE( get_votes( "witness2" ) == ( votes_witness2 + witness2_result_00.value ) ); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*11*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*11*/ ), true ); auto _v5 = vamount( _5 ); vest( "alice", "alice", _5, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_30, "alice", _v5, day, minutes ) ); - fill( proxy_30, "alice", day - diff, minutes ); + BOOST_REQUIRE( cmp( proxy_30, "alice", _v5, nr_interval, seconds ) ); + fill( proxy_30, "alice", nr_interval - diff, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*12*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*12*/ ), true ); auto _v1 = vamount( _1 ); vest( "alice", "bob", _1, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_30, "bob", _v1, day, minutes ) ); - fill( proxy_30, "bob", day - diff, minutes ); + BOOST_REQUIRE( cmp( proxy_30, "bob", _v1, nr_interval, seconds ) ); + fill( proxy_30, "bob", nr_interval - diff, seconds ); generate_block(); - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*13*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*13*/ ), true ); auto _v2 = vamount( _2 ); vest( "alice", "celine", _2, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_30, "celine", _v2, day, minutes ) ); - fill( proxy_30, "celine", day - diff, minutes ); + BOOST_REQUIRE( cmp( proxy_30, "celine", _v2, nr_interval, seconds ) ); + fill( proxy_30, "celine", nr_interval - diff, seconds ); generate_block(); } @@ -659,27 +669,27 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) { /* - *****`+ 31 days`***** + *****`+ 31 nr_intervals`***** `celine` makes vests */ start_time += fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS ) ; generate_blocks( start_time, true ); - day = 31; + nr_interval = 31; size_t diff = 2;//because `2` elements in delayed_votes have been removed already - ++minutes; - generate_blocks( start_time + fc::minutes( minutes/*14*/ ), true ); + seconds += 3; + generate_blocks( start_time + fc::seconds( seconds/*14*/ ), true ); auto _v5 = vamount( _5 ); vest( "alice", "celine", _5, alice_private_key ); - BOOST_REQUIRE( cmp( proxy_31, "celine", _v5, day, minutes ) ); - fill( proxy_31, "celine", day - diff, minutes ); + BOOST_REQUIRE( cmp( proxy_31, "celine", _v5, nr_interval, seconds ) ); + fill( proxy_31, "celine", nr_interval - diff, seconds ); generate_block(); } { /* - *****`lasted 31 days`***** + *****`lasted 31 nr_intervals`***** */ - start_time += fc::hours( 23 ) + fc::minutes( 59 ); + start_time += fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS ) - fc::seconds( 30 ); generate_blocks( start_time, true ); BOOST_REQUIRE( witness1_result_01 > 0 ); @@ -691,12 +701,12 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) idump( (votes_witness1) ); idump( (witness1_result_00) ); idump( (witness1_result_01) ); - BOOST_REQUIRE( get_votes( "witness1" ) == ( votes_witness1 + ( witness1_result_00 + witness1_result_01 ).value ) ); + BOOST_REQUIRE_EQUAL( get_votes( "witness1" ).value, ( votes_witness1 + ( witness1_result_00 + witness1_result_01 ).value ).value ); idump( (votes_witness2) ); idump( (witness2_result_00) ); idump( (witness2_result_01) ); - BOOST_REQUIRE( get_votes( "witness2" ) == ( votes_witness2 + ( witness2_result_00 + witness2_result_01 ).value ) ); + BOOST_REQUIRE_EQUAL( get_votes( "witness2" ).value, ( votes_witness2 + ( witness2_result_00 + witness2_result_01 ).value ).value ); start_time += fc::minutes( 1 ); generate_blocks( start_time, true ); @@ -707,9 +717,9 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) { /* - *****`lasted 32 days`***** + *****`lasted 32 nr_intervals`***** */ - start_time += fc::hours( 23 ) + fc::minutes( 59 ); + start_time += fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS ) - fc::seconds( 30 ); generate_blocks( start_time, true ); BOOST_REQUIRE( witness1_result_02 > 0 ); @@ -735,7 +745,7 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) } { /* - *****`lasted 40 days`***** + *****`lasted 40 nr_intervals`***** */ start_time += 8 * HIVE_DELAYED_VOTING_INTERVAL_SECONDS; generate_blocks( start_time, true ); @@ -780,7 +790,7 @@ BOOST_AUTO_TEST_CASE( delayed_voting_proxy_02 ) { /* - *****`lasted 80 days`***** + *****`lasted 80 nr_intervals`***** */ start_time += 40 * HIVE_DELAYED_VOTING_INTERVAL_SECONDS; generate_blocks( start_time, true ); @@ -1197,10 +1207,10 @@ BOOST_AUTO_TEST_CASE( delayed_voting_05 ) BOOST_REQUIRE( basic_votes_1 == votes_01_1 ); BOOST_REQUIRE( basic_votes_2 == votes_01_2 ); - for(int i = 1; i < DAYS_FOR_DELAYED_VOTING - 1; i++) + for(int i = 1; i < NR_INTERVALS_IN_DELAYED_VOTING - 1; i++) { generate_blocks( start_time + (i * HIVE_DELAYED_VOTING_INTERVAL_SECONDS) , true ); - if( i == static_cast( DAYS_FOR_DELAYED_VOTING/2 )) witness_vote( "bob", "witness2", true/*approve*/, bob_private_key ); + if( i == static_cast( NR_INTERVALS_IN_DELAYED_VOTING/2 )) witness_vote( "bob", "witness2", true/*approve*/, bob_private_key ); BOOST_REQUIRE( get_votes( "witness1" ) == votes_01_1 ); BOOST_REQUIRE( get_votes( "witness2" ) == votes_01_2 ); } @@ -1292,7 +1302,7 @@ BOOST_AUTO_TEST_CASE( delayed_voting_basic_06 ) for( uint32_t i = 0; i < 10; ++i ) { - int64_t cnt = 0; + size_t cnt = 0; for( auto& item : accs ) { const uint64_t pre_size{ withdraw_items->size() }; @@ -1359,7 +1369,7 @@ BOOST_AUTO_TEST_CASE( delayed_voting_basic_05 ) { for( uint32_t i = 0; i < 29; ++i ) { - int64_t cnt = 0; + size_t cnt = 0; for( auto& item : accs ) { dv.add_delayed_value( db->get_account( item ), start_time + fc::days(1), 10'000 ); @@ -1401,6 +1411,9 @@ BOOST_AUTO_TEST_CASE( delayed_voting_basic_03 ) { try { + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + // support function const auto get_delayed_vote_count = [&]( const account_name_type& name = "bob", const std::vector& data_to_compare ) { @@ -1421,8 +1434,6 @@ BOOST_AUTO_TEST_CASE( delayed_voting_basic_03 ) const auto start_time = db->head_block_time(); ACTORS( (alice)(celine)(bob)(witness) ) generate_block(); - set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); - generate_block(); FUND( "bob", ASSET( "100000.000 TESTS" ) ); FUND( "celine", ASSET( "100000.000 TESTS" ) ); FUND( "alice", ASSET( "100000.000 TESTS" ) ); @@ -1456,7 +1467,7 @@ BOOST_AUTO_TEST_CASE( delayed_voting_basic_03 ) // check everyday for month bool s = false; - for(int i = 1; i < DAYS_FOR_DELAYED_VOTING - 1; i++) + for(int i = 1; i < NR_INTERVALS_IN_DELAYED_VOTING - 1; i++) { // 1 day generate_blocks( start_time + (i * HIVE_DELAYED_VOTING_INTERVAL_SECONDS) , true ); @@ -1541,7 +1552,7 @@ BOOST_AUTO_TEST_CASE( delayed_voting_basic_04 ) move_forward( fc::days( 4 ) ); } { - int64_t cnt = 0; + size_t cnt = 0; delayed_voting::opt_votes_update_data_items withdraw_items = delayed_voting::votes_update_data_items(); for( auto& item : accs ) @@ -1587,10 +1598,10 @@ BOOST_AUTO_TEST_CASE( delayed_voting_basic_02 ) BOOST_TEST_MESSAGE( "Testing: `delayed_voting::update_votes` method" ); - ACTORS( (alice)(bob) ) + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); generate_block(); - set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + ACTORS( (alice)(bob) ) generate_block(); FUND( "alice", ASSET( "10000.000 TESTS" ) ); @@ -1611,7 +1622,7 @@ BOOST_AUTO_TEST_CASE( delayed_voting_basic_02 ) delayed_voting dv = delayed_voting( *db ); - fc::time_point_sec time = db->head_block_time() + fc::hours( 5 ); + fc::time_point_sec time = db->head_block_time() + fc::minutes( 5 ); auto& __alice = db->get_account( "alice" ); auto alice_dv = __alice.delayed_votes; @@ -1879,70 +1890,70 @@ BOOST_AUTO_TEST_CASE( delayed_voting_processor_01 ) BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 )/*time*/, 1ul/*val*/ ) ); } { - delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::hours( 1 )/*time*/, 2ul/*val*/ ); + delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::minutes( 30 )/*time*/, 2ul/*val*/ ); BOOST_REQUIRE( dq.size() == 1 ); BOOST_REQUIRE( sum == 3 ); BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 )/*time*/, 3/*val*/ ) ); } { - delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::hours( 24 ) - fc::seconds( 1 )/*time*/, 3ul/*val*/ ); + delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS ) - fc::seconds( 3 )/*time*/, 3ul/*val*/ ); BOOST_REQUIRE( dq.size() == 1 ); BOOST_REQUIRE( sum == 6 ); BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 )/*time*/, 6/*val*/ ) ); } { - delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::hours( 24 )/*time*/, 4ul/*val*/ ); + delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 4ul/*val*/ ); BOOST_REQUIRE( dq.size() == 2 ); BOOST_REQUIRE( sum == 10 ); BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 )/*time*/, 6/*val*/ ) ); - BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::hours( 24 )/*time*/, 4/*val*/ ) ); + BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 4/*val*/ ) ); } { - delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::hours( 2*24 )/*time*/, 5ul/*val*/ ); + delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::seconds( 2*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 5ul/*val*/ ); BOOST_REQUIRE( dq.size() == 3 ); BOOST_REQUIRE( sum == 15 ); BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 )/*time*/, 6/*val*/ ) ); - BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::hours( 24 )/*time*/, 4/*val*/ ) ); - BOOST_REQUIRE( cmp( 2/*idx*/, time + fc::minutes( 1 ) + fc::hours( 2*24 )/*time*/, /*val*/5 ) ); + BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 4/*val*/ ) ); + BOOST_REQUIRE( cmp( 2/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 2*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, /*val*/5 ) ); } { delayed_voting_processor::erase_front( dq, sum ); BOOST_REQUIRE( dq.size() == 2 ); BOOST_REQUIRE( sum == 9 ); - BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::hours( 24 )/*time*/, 4/*val*/ ) ); - BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::hours( 2*24 )/*time*/, 5/*val*/ ) ); + BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::seconds( HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 4/*val*/ ) ); + BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 2*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 5/*val*/ ) ); } { delayed_voting_processor::erase_front( dq, sum ); BOOST_REQUIRE( dq.size() == 1 ); BOOST_REQUIRE( sum == 5 ); - BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::hours( 2*24 )/*time*/, 5/*val*/ ) ); + BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 2*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 5/*val*/ ) ); } { - delayed_voting_processor::add( dq, sum, time + fc::minutes( 2 ) + fc::hours( 2*24 )/*time*/, 6/*val*/ ); + delayed_voting_processor::add( dq, sum, time + fc::minutes( 2 ) + fc::seconds( 2*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 6/*val*/ ); BOOST_REQUIRE( dq.size() == 1 ); BOOST_REQUIRE( sum == 11 ); - BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::hours( 2*24 )/*time*/, 11/*val*/ ) ); + BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 2*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 11/*val*/ ) ); } { - delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::hours( 3*24 )/*time*/, 7/*val*/ ); + delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::seconds( 3*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 7/*val*/ ); BOOST_REQUIRE( dq.size() == 2 ); BOOST_REQUIRE( sum == 18 ); - BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::hours( 2*24 )/*time*/, 11/*val*/ ) ); - BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::hours( 3*24 )/*time*/, 7/*val*/ ) ); + BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 2*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 11/*val*/ ) ); + BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 3*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 7/*val*/ ) ); } { - delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::hours( 3*24 ) + fc::seconds( 3 )/*time*/, 8/*val*/ ); + delayed_voting_processor::add( dq, sum, time + fc::minutes( 1 ) + fc::seconds( 3*HIVE_DELAYED_VOTING_INTERVAL_SECONDS ) + fc::seconds( 3 )/*time*/, 8/*val*/ ); BOOST_REQUIRE( dq.size() == 2 ); BOOST_REQUIRE( sum == 26 ); - BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::hours( 2*24 )/*time*/, 11/*val*/ ) ); - BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::hours( 3*24 )/*time*/, 15/*val*/ ) ); + BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 2*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 11/*val*/ ) ); + BOOST_REQUIRE( cmp( 1/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 3*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 15/*val*/ ) ); } { delayed_voting_processor::erase_front( dq, sum ); BOOST_REQUIRE( dq.size() == 1 ); BOOST_REQUIRE( sum == 15 ); - BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::hours( 3*24 )/*time*/, 15/*val*/ ) ); + BOOST_REQUIRE( cmp( 0/*idx*/, time + fc::minutes( 1 ) + fc::seconds( 3*HIVE_DELAYED_VOTING_INTERVAL_SECONDS )/*time*/, 15/*val*/ ) ); } { delayed_voting_processor::erase_front( dq, sum ); @@ -2185,6 +2196,9 @@ BOOST_AUTO_TEST_CASE( decline_voting_rights_04 ) { BOOST_TEST_MESSAGE( "Testing: decline voting rights: casual use with cancel" ); + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + const std::function is_cancelled = [&](const account_name_type name) { const auto& request_idx = db->get_index< decline_voting_rights_request_index >().indices().get< by_account >(); @@ -2194,9 +2208,6 @@ BOOST_AUTO_TEST_CASE( decline_voting_rights_04 ) ACTORS( (bob)(alice)(witness) ) generate_block(); - set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); - generate_block(); - // auto start_time = db->head_block_time(); FUND( "bob", ASSET( "10000.000 TESTS" ) ); FUND( "alice", ASSET( "10000.000 TESTS" ) ); @@ -2247,10 +2258,10 @@ BOOST_AUTO_TEST_CASE( small_common_test_01 ) { BOOST_TEST_MESSAGE( "Testing: simulation of trying to overcome system" ); - ACTORS( (alice)(witness) ) + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); generate_block(); - set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + ACTORS( (alice)(witness) ) generate_block(); //auto start_time = db->head_block_time(); @@ -2275,7 +2286,7 @@ BOOST_AUTO_TEST_CASE( small_common_test_01 ) share_type votes_01 = get_votes( "witness" ); BOOST_REQUIRE( votes_01 == basic_votes ); - generate_blocks( start_time + fc::days(DAYS_FOR_DELAYED_VOTING - 1) , true ); + generate_blocks( start_time + fc::seconds( (NR_INTERVALS_IN_DELAYED_VOTING - 1) * HIVE_DELAYED_VOTING_INTERVAL_SECONDS ) , true ); generate_block(); // just before lock down alice vests huge amount of TESTs @@ -2306,8 +2317,8 @@ BOOST_AUTO_TEST_CASE( small_common_test_01 ) #define WITNESS_VOTES( witness ) db->get_witness( witness ).votes.value -#define DAY_REPORT( day ) \ - BOOST_TEST_MESSAGE( "[scenario_01]: current day: " << day ); \ +#define INTERVAL_REPORT( day ) \ + BOOST_TEST_MESSAGE( "[scenario_01]: current interval: " << day ); \ ACCOUNT_REPORT( "alice" ); \ ACCOUNT_REPORT( "alice0bp" ); \ BOOST_TEST_MESSAGE( "[scenario_01]: " << "alice0bp" << " has " << WITNESS_VOTES( "alice0bp" ) << " votes" ); \ @@ -2343,11 +2354,11 @@ BOOST_AUTO_TEST_CASE( small_common_test_01 ) BOOST_REQUIRE( DELAYED_VOTES( "bob0bp" ) == expected_bob0bp_delayed_votes ); \ BOOST_REQUIRE( VOTING_POWER( "bob" ) == WITNESS_VOTES( "bob0bp" ) ) -#define GOTO_DAY( day ) \ - generate_days_blocks( day - today ); \ - today = day +#define GOTO_INTERVAL( interval ) \ + generate_seconds_blocks( ( interval * (HIVE_DELAYED_VOTING_INTERVAL_SECONDS) ) - today ); \ + today = ( interval * (HIVE_DELAYED_VOTING_INTERVAL_SECONDS) ) -BOOST_AUTO_TEST_CASE( scenario_01 ) +BOOST_AUTO_TEST_CASE( scenario_01, *boost::unit_test::disabled()) { try { @@ -2458,27 +2469,27 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) vest( "alice", "alice", ASSET( "1000.000 TESTS" ), alice_private_key ); validate_database(); - DAY_REPORT( today ); + INTERVAL_REPORT( today ); BOOST_REQUIRE( get_balance( "alice" ) == ASSET( "300.000 TESTS" ) ); /* Day 5: alice powers up 300 HIVE; she has 1300 vests, including delayed 1000 maturing on day 30 and 300 maturing on day 35, 0 HIVE */ BOOST_TEST_MESSAGE( "[scenario_01]: before set_current_day( 5 ): " << get_current_time_iso_string() ); - GOTO_DAY( 5 ); + GOTO_INTERVAL( 5 ); BOOST_TEST_MESSAGE( "[scenario_01]: after set_current_day( 5 ): " << get_current_time_iso_string() ); BOOST_TEST_MESSAGE( "[scenario_01]: alice powers up 300" ); vest( "alice", "alice", ASSET( "300.000 TESTS" ), alice_private_key ); validate_database(); - DAY_REPORT( today ); + INTERVAL_REPORT( today ); BOOST_REQUIRE( get_balance( "alice" ) == ASSET( "0.000 TESTS" ) ); /* Day 10: alice powers down 1300 vests; this schedules virtual PD actions on days 17, 24, 31, 38, 45, 52, 59, 66, 73, 80, 87, 94 and 101 */ - GOTO_DAY( 10 ); + GOTO_INTERVAL( 10 ); int64_t new_vests = (get_vesting( "alice" ) - origin_alice_vests).amount.value; int64_t new_vests_portion = new_vests / HIVE_VESTING_WITHDRAW_INTERVALS; @@ -2490,7 +2501,7 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) withdraw_vesting( "alice", asset( new_vests, VESTS_SYMBOL ), alice_private_key ); validate_database(); - DAY_REPORT( today ); + INTERVAL_REPORT( today ); BOOST_REQUIRE( get_balance( "alice" ) == ASSET( "0.000 TESTS" ) ); /* @@ -2523,8 +2534,8 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) expected_bob0bp_delayed_votes = initial_bob0bp_delayed_votes; expected_carol_hive = initial_carol_hive; - GOTO_DAY( 17 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 17 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( new_vests_portion + 1, VESTS_SYMBOL ); expected_alice_vests += asset( portion, VESTS_SYMBOL ); @@ -2536,8 +2547,8 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 24: PD of 100 vests from alice split like above alice 50S+1150v=0+1000(30d)+150(35d); bob 50v=0+25(47d)+25(54d); carol 50S */ - GOTO_DAY( 24 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 24 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 2 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 2 * portion, VESTS_SYMBOL ); @@ -2548,8 +2559,8 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 30: 1000 vests matures on alice, alice.bp receives 1000 vests of new voting power (1000v) alice 50S+1150v=1000+150(35d); bob 50v=0+25(47d)+25(54d); carol 50S */ - GOTO_DAY( 30 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 30 ); + INTERVAL_REPORT( today ); expected_alice0bp_delayed_votes = 0; expected_bob0bp_delayed_votes = 0; @@ -2559,8 +2570,8 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 31: PD of 100 vests from alice alice 75S+1075v=1000+75(35d); bob 75v=0+25(47d)+25(54d)+25(61d); carol 75S */ - GOTO_DAY( 31 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 31 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 3 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 3 * portion, VESTS_SYMBOL ); @@ -2571,16 +2582,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 35: remaining 75 vests mature on alice, alice.bp receives 75 vests of new voting power (1075v) alice 75S+1075v=1075; bob 75v=0+25(47d)+25(54d)+25(61d); carol 75S */ - GOTO_DAY( 35 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 35 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 38: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (1000v) alice 100S+1000v=1000; bob 100v=0+25(47d)+25(54d)+25(61d)+25(68d); carol 100S */ - GOTO_DAY( 38 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 38 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 4 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 4 * portion, VESTS_SYMBOL ); @@ -2591,8 +2602,8 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 45: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (925v) alice 125S+925v=925; bob 125v=0+25(47d)+25(54d)+25(61d)+25(68d)+25(75d); carol 125S */ - GOTO_DAY( 45 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 45 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 5 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 5 * portion, VESTS_SYMBOL ); @@ -2603,16 +2614,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 47: first 25 vests mature on bob, bob.bp receives 25 vests of new voting power (25v) alice 125S+925v=925; bob 125v=25+25(54d)+25(61d)+25(68d)+25(75d); carol 125S */ - GOTO_DAY( 47 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 47 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 52: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (850v) alice 150S+850v=850; bob 150v=25+25(54d)+25(61d)+25(68d)+25(75d)+25(82d); carol 150S */ - GOTO_DAY( 52 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 52 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 6 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 6 * portion, VESTS_SYMBOL ); @@ -2623,16 +2634,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 54: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (50v) alice 150S+850v=850; bob 150v=50+25(61d)+25(68d)+25(75d)+25(82d); carol 150S */ - GOTO_DAY( 54 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 54 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 59: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (775v) alice 175S+775v=775; bob 175v=50+25(61d)+25(68d)+25(75d)+25(82d)+25(89d); carol 175S */ - GOTO_DAY( 59 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 59 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 7 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 7 * portion, VESTS_SYMBOL ); @@ -2643,16 +2654,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 61: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (75v) alice 175S+775v=775; bob 175v=75+25(68d)+25(75d)+25(82d)+25(89d); carol 175S */ - GOTO_DAY( 61 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 61 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 66: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (700v) alice 200S+700v=700; bob 200v=75+25(68d)+25(75d)+25(82d)+25(89d)+25(96d); carol 200S */ - GOTO_DAY( 66 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 66 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 8 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 8 * portion, VESTS_SYMBOL ); @@ -2663,16 +2674,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 68: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (100v) alice 200S+700v=700; bob 200v=100+25(75d)+25(82d)+25(89d)+25(96d); carol 200S */ - GOTO_DAY( 68 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 68 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 73: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (625v) alice 225S+625v=625; bob 225v=100+25(75d)+25(82d)+25(89d)+25(96d)+25(103d); carol 225S */ - GOTO_DAY( 73 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 73 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 9 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 9 * portion, VESTS_SYMBOL ); @@ -2683,16 +2694,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 75: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (125v) alice 225S+625v=625; bob 225v=125+25(82d)+25(89d)+25(96d)+25(103d); carol 225S */ - GOTO_DAY( 75 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 75 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 80: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (550v) alice 250S+550v=550; bob 250v=125+25(82d)+25(89d)+25(96d)+25(103d)+25(110d); carol 250S */ - GOTO_DAY( 80 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 80 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 10 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 10 * portion, VESTS_SYMBOL ); @@ -2703,16 +2714,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 82: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (150v) alice 250S+550v=550; bob 250v=150+25(89d)+25(96d)+25(103d)+25(110d); carol 250S */ - GOTO_DAY( 82 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 82 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 87: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (475v) alice 275S+475v=475; bob 275v=150+25(89d)+25(96d)+25(103d)+25(110d)+25(117d); carol 275S */ - GOTO_DAY( 87 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 87 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 11 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 11 * portion, VESTS_SYMBOL ); @@ -2723,16 +2734,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 89: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (175v) alice 275S+475v=475; bob 275v=175+25(96d)+25(103d)+25(110d)+25(117d); carol 275S */ - GOTO_DAY( 89 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 89 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 94: PD of 100 vests from alice, alice.bp loses 75 vests of voting power (400v) alice 300S+400v=400; bob 300v=175+25(96d)+25(103d)+25(110d)+25(117d)+25(124d); carol 300S */ - GOTO_DAY( 94 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 94 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( 12 * (new_vests_portion + 1), VESTS_SYMBOL ); expected_alice_vests += asset( 12 * portion, VESTS_SYMBOL ); @@ -2743,16 +2754,16 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 96: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (200v) alice 300S+400v=400; bob 300v=200+25(103d)+25(110d)+25(117d)+25(124d); carol 300S */ - GOTO_DAY( 96 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 96 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 101: last scheduled PD of 100 vests from alice, alice.bp loses 75 vests of voting power (325v) alice 325S+325v=325; bob 325v=200+25(103d)+25(110d)+25(117d)+25(124d)+25(131d); carol 325S */ - GOTO_DAY( 101 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 101 ); + INTERVAL_REPORT( today ); expected_alice_vests = initial_alice_vests - asset( new_vests, VESTS_SYMBOL ); expected_alice_vests += asset( quarter - 3, VESTS_SYMBOL ); @@ -2764,40 +2775,40 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) Day 103: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (225v) alice 325S+325v=325; bob 325v=225+25(110d)+25(117d)+25(124d)+25(131d); carol 325S */ - GOTO_DAY( 103 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 103 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 110: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (250v) alice 325S+325v=325; bob 325v=250+25(117d)+25(124d)+25(131d); carol 325S */ - GOTO_DAY( 110 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 110 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 117: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (275v) alice 325S+325v=325; bob 325v=275+25(124d)+25(131d); carol 325S */ - GOTO_DAY( 117 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 117 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 124: 25 vests mature on bob, bob.bp receives 25 vests of new voting power (300v) alice 325S+325v=325; bob 325v=300+25(131d); carol 325S */ - GOTO_DAY( 124 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 124 ); + INTERVAL_REPORT( today ); DAY_CHECK; /* Day 131: last 25 vests mature on bob, bob.bp receives 25 vests of new voting power (325v) alice 325S+325v=325; bob 325v=325; carol 325S */ - GOTO_DAY( 131 ); - DAY_REPORT( today ); + GOTO_INTERVAL( 131 ); + INTERVAL_REPORT( today ); DAY_CHECK; } @@ -2809,13 +2820,13 @@ BOOST_AUTO_TEST_CASE( scenario_01 ) #undef ACCOUNT_REPORT #undef WITNESS_VOTES #undef DELAYED_VOTES -#undef DAY_REPORT +#undef INTERVAL_REPORT #undef CHECK_ACCOUNT_VESTS #undef CHECK_ACCOUNT_HIVE #undef CHECK_ACCOUNT_VP #undef CHECK_WITNESS_VOTES #undef DAY_CHECK -#undef GOTO_DAY +#undef GOTO_INTERVAL BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit/tests/hf23_tests.cpp b/tests/unit/tests/hf23_tests.cpp index a78aa611c6..fbf677f732 100644 --- a/tests/unit/tests/hf23_tests.cpp +++ b/tests/unit/tests/hf23_tests.cpp @@ -1158,6 +1158,26 @@ BOOST_AUTO_TEST_CASE( convert_request_cleanup_test ) REQUIRE_BALANCE( "0.000", "5.100", get_balance, "TESTS" ); REQUIRE_BALANCE( "0.000", "0.063", get_hbd_balance, "TBD" ); UNDO_CLEAR; + + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + { + collateralized_convert_operation op; + op.owner = "alice"; + op.amount = ASSET( "1.000 TESTS" ); + tx.operations.push_back( op ); + sign( tx, alice_private_key ); + db->push_transaction( tx, 0 ); + } + tx.clear(); + + //collateralized conversion was not present when account clearing was executed, clear_account is not prepared for it + //note that if you decide to handle it, you have to consider that HBD was produced from thin air and was already transfered, + //while collateral that should be partially burned during actual conversion is still in global supply; in other words you + //should actually finish conversion instead of passing collateral to treasury, because otherwise the temporary state of + //having more HIVE than is proper, will become permanent + HIVE_REQUIRE_THROW( db->clear_account( db->get_account( "alice" ) ), fc::exception ); + + database_fixture::validate_database(); } FC_LOG_AND_RETHROW() @@ -1223,16 +1243,27 @@ BOOST_AUTO_TEST_CASE( hbd_test_02 ) } auto& gpo = db->get_dynamic_global_properties(); - auto interest_op = get_last_operations( 1 )[0].get< interest_operation >(); - - BOOST_REQUIRE( gpo.get_hbd_interest_rate() > 0 ); - BOOST_REQUIRE( static_cast(get_hbd_balance( "alice" ).amount.value) == - alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value + - ( ( ( ( uint128_t( alice_hbd.amount.value ) * ( db->head_block_time() - start_time ).to_seconds() ) / HIVE_SECONDS_PER_YEAR ) * - gpo.get_hbd_interest_rate() ) / HIVE_100_PERCENT ).to_uint64() ); - BOOST_REQUIRE( interest_op.owner == "alice" ); - BOOST_REQUIRE( interest_op.interest.amount.value == - get_hbd_balance( "alice" ).amount.value - ( alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value ) ); + + BOOST_REQUIRE(gpo.get_hbd_interest_rate() > 0); + + if(db->has_hardfork(HIVE_HARDFORK_1_25)) + { + /// After HF 25 only HBD held on savings should get interest + BOOST_REQUIRE(get_hbd_balance("alice").amount.value == alice_hbd.amount.value - ASSET("1.000 TBD").amount.value ); + } + else + { + auto interest_op = get_last_operations( 1 )[0].get< interest_operation >(); + + BOOST_REQUIRE( static_cast(get_hbd_balance( "alice" ).amount.value) == + alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value + + ( ( ( ( uint128_t( alice_hbd.amount.value ) * ( db->head_block_time() - start_time ).to_seconds() ) / HIVE_SECONDS_PER_YEAR ) * + gpo.get_hbd_interest_rate() ) / HIVE_100_PERCENT ).to_uint64() ); + BOOST_REQUIRE( interest_op.owner == "alice" ); + BOOST_REQUIRE( interest_op.interest.amount.value == + get_hbd_balance( "alice" ).amount.value - ( alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value ) ); + } + database_fixture::validate_database(); generate_blocks( db->head_block_time() + fc::seconds( HIVE_HBD_INTEREST_COMPOUND_INTERVAL_SEC ), true ); @@ -1243,6 +1274,7 @@ BOOST_AUTO_TEST_CASE( hbd_test_02 ) db->clear_account( db->get_account( "alice" ) ); BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "0.000 TBD" ) ); + database_fixture::validate_database(); } FC_LOG_AND_RETHROW() diff --git a/tests/unit/tests/operation_tests.cpp b/tests/unit/tests/operation_tests.cpp index d9f028a205..402c2a8d31 100644 --- a/tests/unit/tests/operation_tests.cpp +++ b/tests/unit/tests/operation_tests.cpp @@ -1,5 +1,6 @@ #ifdef IS_TEST_NET #include +#include #include @@ -29,8 +30,10 @@ using namespace hive::chain; using namespace hive::protocol; using fc::string; -#define VOTING_MANABAR( account ) db->get_account( account ).voting_manabar -#define DOWNVOTE_MANABAR( account ) db->get_account( account ).downvote_manabar +#define VOTING_MANABAR( account_name ) db->get_account( account_name ).voting_manabar +#define DOWNVOTE_MANABAR( account_name ) db->get_account( account_name ).downvote_manabar +#define CHECK_PROXY( account, proxy ) BOOST_REQUIRE( account.get_proxy() == proxy.get_id() ) +#define CHECK_NO_PROXY( account ) BOOST_REQUIRE( account.has_proxy() == false ) inline uint16_t get_voting_power( const account_object& a ) { @@ -134,7 +137,7 @@ BOOST_AUTO_TEST_CASE( account_create_apply ) BOOST_REQUIRE( acct_auth.owner == authority( 1, priv_key.get_public_key(), 1 ) ); BOOST_REQUIRE( acct_auth.active == authority( 2, priv_key.get_public_key(), 2 ) ); BOOST_REQUIRE( acct.memo_key == priv_key.get_public_key() ); - BOOST_REQUIRE( acct.proxy == "" ); + CHECK_NO_PROXY( acct ); BOOST_REQUIRE( acct.created == db->head_block_time() ); BOOST_REQUIRE( acct.get_balance().amount.value == ASSET( "0.000 TESTS" ).amount.value ); BOOST_REQUIRE( acct.get_hbd_balance().amount.value == ASSET( "0.000 TBD" ).amount.value ); @@ -153,7 +156,7 @@ BOOST_AUTO_TEST_CASE( account_create_apply ) BOOST_REQUIRE( acct_auth.owner == authority( 1, priv_key.get_public_key(), 1 ) ); BOOST_REQUIRE( acct_auth.active == authority( 2, priv_key.get_public_key(), 2 ) ); BOOST_REQUIRE( acct.memo_key == priv_key.get_public_key() ); - BOOST_REQUIRE( acct.proxy == "" ); + CHECK_NO_PROXY( acct ); BOOST_REQUIRE( acct.created == db->head_block_time() ); BOOST_REQUIRE( acct.get_balance().amount.value == ASSET( "0.000 TESTS " ).amount.value ); BOOST_REQUIRE( acct.get_hbd_balance().amount.value == ASSET( "0.000 TBD" ).amount.value ); @@ -202,7 +205,7 @@ BOOST_AUTO_TEST_CASE( account_create_apply ) tx.operations.push_back( op ); db->push_transaction( tx, 0 ); - BOOST_REQUIRE( db->get_account( "bob" ).recovery_account == account_name_type() ); + BOOST_REQUIRE( !db->get_account( "bob" ).has_recovery_account() ); validate_database(); } @@ -491,16 +494,6 @@ BOOST_AUTO_TEST_CASE( comment_apply ) BOOST_REQUIRE( alice_comment_cashout->abs_rshares.value == 0 ); BOOST_REQUIRE( alice_comment_cashout->cashout_time == fc::time_point_sec( db->head_block_time() + fc::seconds( HIVE_CASHOUT_WINDOW_SECONDS ) ) ); - #if !defined(IS_LOW_MEM) && defined(STORE_COMMENT_CONTENT) - const auto& alice_comment_content = db->get< comment_content_object, by_comment >( alice_comment.get_id() ); - BOOST_REQUIRE( to_string( alice_comment_content.title ) == op.title ); - BOOST_REQUIRE( to_string( alice_comment_content.body ) == op.body ); - BOOST_REQUIRE( to_string( alice_comment_content.json_metadata ) == op.json_metadata ); - #else - const auto* alice_comment_content = db->find< comment_content_object, by_comment >( alice_comment.get_id() ); - BOOST_REQUIRE( alice_comment_content == nullptr ); - #endif - validate_database(); BOOST_TEST_MESSAGE( "--- Test Bob posting a comment on a non-existent comment" ); @@ -2280,6 +2273,72 @@ BOOST_AUTO_TEST_CASE(account_witness_vote_apply_delay) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( account_object_by_governance_vote_expiration_ts_idx ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: account_object_by_governance_vote_expiration_ts_idx" ); + + ACTORS( (acc1)(acc2)(acc3)(acc4)(accw) ) + signed_transaction tx; + private_key_type accw_witness_key = generate_private_key( "accw_key" ); + witness_create( "accw", accw_private_key, "foo.bar", accw_witness_key.get_public_key(), 1000 ); + + //Cannot use vote_proposal() and witness_vote() because of differ DB Fixture + auto witness_vote = [&](std::string voter, const fc::ecc::private_key& key) { + account_witness_vote_operation op; + op.account = voter; + op.witness = "accw"; + op.approve = true; + + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + tx.operations.push_back( op ); + sign( tx, key ); + db->push_transaction( tx, 0 ); + tx.clear(); + }; + + auto proposal_vote = [&](std::string voter, const std::vector& proposals, const fc::ecc::private_key& key) { + update_proposal_votes_operation op; + op.voter = voter; + op.proposal_ids.insert(proposals.cbegin(), proposals.cend()); //doesn't matter which ids, order in the container matters in this test case. + op.approve = true; + + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + tx.operations.push_back( op ); + sign( tx, key ); + db->push_transaction( tx, 0 ); + tx.clear(); + }; + + generate_block(); + witness_vote("acc1", acc1_private_key); + generate_block(); + witness_vote("acc2", acc2_private_key); + proposal_vote("acc3", {154,357,987}, acc3_private_key); + generate_block(); + proposal_vote("acc4", {111,222,333,444,555}, acc4_private_key); + generate_block(); + + + BOOST_REQUIRE (db->get_account( "acc1" ).get_governance_vote_expiration_ts() != db->get_account( "acc2" ).get_governance_vote_expiration_ts()); + BOOST_REQUIRE (db->get_account( "acc2" ).get_governance_vote_expiration_ts() == db->get_account( "acc3" ).get_governance_vote_expiration_ts()); + BOOST_REQUIRE (db->get_account( "acc4" ).get_governance_vote_expiration_ts() != db->get_account( "acc3" ).get_governance_vote_expiration_ts()); + + const auto& accounts = db->get_index< account_index, by_governance_vote_expiration_ts >(); + time_point_sec governance_vote_expiration_ts = accounts.begin()->get_governance_vote_expiration_ts(); + + for (const auto&ac : accounts) + { + time_point_sec curr_last_vote = ac.get_governance_vote_expiration_ts(); + BOOST_REQUIRE (governance_vote_expiration_ts <= curr_last_vote); + governance_vote_expiration_ts = curr_last_vote; + } + validate_database(); + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_CASE( account_witness_proxy_validate ) { try @@ -2371,9 +2430,9 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( bob.proxy == "alice" ); + CHECK_PROXY( bob, alice ); BOOST_REQUIRE( bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( alice.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( alice ); BOOST_REQUIRE( alice.proxied_vsf_votes_total() == bob.get_real_vesting_shares() ); validate_database(); @@ -2388,10 +2447,10 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( bob.proxy == "sam" ); + CHECK_PROXY( bob, sam ); BOOST_REQUIRE( bob.proxied_vsf_votes_total().value == 0 ); BOOST_REQUIRE( alice.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( sam.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( sam ); BOOST_REQUIRE( sam.proxied_vsf_votes_total().value == bob.get_real_vesting_shares() ); validate_database(); @@ -2399,9 +2458,9 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply ) HIVE_REQUIRE_THROW( db->push_transaction( tx, database::skip_transaction_dupe_check ), fc::exception ); - BOOST_REQUIRE( bob.proxy == "sam" ); + CHECK_PROXY( bob, sam ); BOOST_REQUIRE( bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( sam.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( sam ); BOOST_REQUIRE( sam.proxied_vsf_votes_total() == bob.get_real_vesting_shares() ); validate_database(); @@ -2417,11 +2476,11 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( bob.proxy == "sam" ); + CHECK_PROXY( bob, sam ); BOOST_REQUIRE( bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( sam.proxy == "dave" ); + CHECK_PROXY( sam, dave ); BOOST_REQUIRE( sam.proxied_vsf_votes_total() == bob.get_real_vesting_shares() ); - BOOST_REQUIRE( dave.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( dave ); BOOST_REQUIRE( dave.proxied_vsf_votes_total() == ( sam.get_real_vesting_shares() + bob.get_real_vesting_shares() ) ); validate_database(); @@ -2439,13 +2498,13 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( alice.proxy == "sam" ); + CHECK_PROXY( alice, sam ); BOOST_REQUIRE( alice.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( bob.proxy == "sam" ); + CHECK_PROXY( bob, sam ); BOOST_REQUIRE( bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( sam.proxy == "dave" ); + CHECK_PROXY( sam, dave ); BOOST_REQUIRE( sam.proxied_vsf_votes_total() == ( bob.get_real_vesting_shares() + alice.get_real_vesting_shares() ) ); - BOOST_REQUIRE( dave.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( dave ); BOOST_REQUIRE( dave.proxied_vsf_votes_total() == ( sam.get_real_vesting_shares() + bob.get_real_vesting_shares() + alice.get_real_vesting_shares() ) ); validate_database(); @@ -2461,13 +2520,13 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( alice.proxy == "sam" ); + CHECK_PROXY( alice, sam ); BOOST_REQUIRE( alice.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( bob.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( bob ); BOOST_REQUIRE( bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( sam.proxy == "dave" ); + CHECK_PROXY( sam, dave ); BOOST_REQUIRE( sam.proxied_vsf_votes_total() == alice.get_real_vesting_shares() ); - BOOST_REQUIRE( dave.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( dave ); BOOST_REQUIRE( dave.proxied_vsf_votes_total() == ( sam.get_real_vesting_shares() + alice.get_real_vesting_shares() ) ); validate_database(); @@ -2551,9 +2610,9 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply_delay ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( _bob.proxy == "alice" ); + CHECK_PROXY( _bob, _alice ); BOOST_REQUIRE( _bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( _alice.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( _alice ); BOOST_REQUIRE( _alice.proxied_vsf_votes_total().value == 0 ); validate_database(); generate_blocks(db->head_block_time() + fc::seconds(HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS)); @@ -2571,10 +2630,10 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply_delay ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( _bob.proxy == "sam" ); + CHECK_PROXY( _bob, _sam ); BOOST_REQUIRE( _bob.proxied_vsf_votes_total().value == 0 ); BOOST_REQUIRE( _alice.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( _sam.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( _sam ); //all vests are now mature so changes in voting power are immediate BOOST_REQUIRE( _sam.proxied_vsf_votes_total().value == _bob.get_vesting().amount ); validate_database(); @@ -2583,9 +2642,9 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply_delay ) HIVE_REQUIRE_THROW( db->push_transaction( tx, database::skip_transaction_dupe_check ), fc::exception ); - BOOST_REQUIRE( _bob.proxy == "sam" ); + CHECK_PROXY( _bob, _sam ); BOOST_REQUIRE( _bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( _sam.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( _sam ); BOOST_REQUIRE( _sam.proxied_vsf_votes_total() == _bob.get_vesting().amount ); validate_database(); @@ -2601,11 +2660,11 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply_delay ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( _bob.proxy == "sam" ); + CHECK_PROXY( _bob, _sam ); BOOST_REQUIRE( _bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( _sam.proxy == "dave" ); + CHECK_PROXY( _sam, _dave ); BOOST_REQUIRE( _sam.proxied_vsf_votes_total() == _bob.get_vesting().amount ); - BOOST_REQUIRE( _dave.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( _dave ); BOOST_REQUIRE( _dave.proxied_vsf_votes_total() == ( _sam.get_vesting() + _bob.get_vesting() ).amount ); validate_database(); @@ -2623,13 +2682,13 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply_delay ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( _alice.proxy == "sam" ); + CHECK_PROXY( _alice, _sam ); BOOST_REQUIRE( _alice.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( _bob.proxy == "sam" ); + CHECK_PROXY( _bob, _sam ); BOOST_REQUIRE( _bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( _sam.proxy == "dave" ); + CHECK_PROXY( _sam, _dave ); BOOST_REQUIRE( _sam.proxied_vsf_votes_total() == ( _bob.get_vesting() + _alice.get_vesting() ).amount ); - BOOST_REQUIRE( _dave.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( _dave ); BOOST_REQUIRE( _dave.proxied_vsf_votes_total() == ( _sam.get_vesting() + _bob.get_vesting() + _alice.get_vesting() ).amount ); validate_database(); @@ -2645,13 +2704,13 @@ BOOST_AUTO_TEST_CASE( account_witness_proxy_apply_delay ) db->push_transaction( tx, 0 ); - BOOST_REQUIRE( _alice.proxy == "sam" ); + CHECK_PROXY( _alice, _sam ); BOOST_REQUIRE( _alice.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( _bob.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( _bob ); BOOST_REQUIRE( _bob.proxied_vsf_votes_total().value == 0 ); - BOOST_REQUIRE( _sam.proxy == "dave" ); + CHECK_PROXY( _sam, _dave ); BOOST_REQUIRE( _sam.proxied_vsf_votes_total() == _alice.get_vesting().amount ); - BOOST_REQUIRE( _dave.proxy == HIVE_PROXY_TO_SELF_ACCOUNT ); + CHECK_NO_PROXY( _dave ); BOOST_REQUIRE( _dave.proxied_vsf_votes_total() == ( _sam.get_vesting() + _alice.get_vesting() ).amount ); validate_database(); @@ -3048,7 +3107,7 @@ BOOST_AUTO_TEST_CASE( convert_apply ) signed_transaction tx; tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); - const auto& convert_request_idx = db->get_index< convert_request_index >().indices().get< by_owner >(); + const auto& convert_request_idx = db->get_index< convert_request_index, by_owner >(); set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); @@ -3103,13 +3162,13 @@ BOOST_AUTO_TEST_CASE( convert_apply ) BOOST_REQUIRE( new_bob.get_balance().amount.value == ASSET( "3.000 TESTS" ).amount.value ); BOOST_REQUIRE( new_bob.get_hbd_balance().amount.value == ASSET( "4.000 TBD" ).amount.value ); - auto convert_request = convert_request_idx.find( boost::make_tuple( op.owner, op.requestid ) ); + auto convert_request = convert_request_idx.find( boost::make_tuple( get_account_id( op.owner ), op.requestid ) ); BOOST_REQUIRE( convert_request != convert_request_idx.end() ); - BOOST_REQUIRE( convert_request->owner == op.owner ); - BOOST_REQUIRE( convert_request->requestid == op.requestid ); - BOOST_REQUIRE( convert_request->amount.amount.value == op.amount.amount.value ); + BOOST_REQUIRE( convert_request->get_owner() == get_account_id( op.owner ) ); + BOOST_REQUIRE( convert_request->get_request_id() == op.requestid ); + BOOST_REQUIRE( convert_request->get_convert_amount().amount.value == op.amount.amount.value ); //BOOST_REQUIRE( convert_request->premium == 100000 ); - BOOST_REQUIRE( convert_request->conversion_date == db->head_block_time() + HIVE_CONVERSION_DELAY ); + BOOST_REQUIRE( convert_request->get_conversion_date() == db->head_block_time() + HIVE_CONVERSION_DELAY ); BOOST_TEST_MESSAGE( "--- Test failure from repeated id" ); op.amount = ASSET( "2.000 TESTS" ); @@ -3122,13 +3181,13 @@ BOOST_AUTO_TEST_CASE( convert_apply ) BOOST_REQUIRE( new_bob.get_balance().amount.value == ASSET( "3.000 TESTS" ).amount.value ); BOOST_REQUIRE( new_bob.get_hbd_balance().amount.value == ASSET( "4.000 TBD" ).amount.value ); - convert_request = convert_request_idx.find( boost::make_tuple( op.owner, op.requestid ) ); + convert_request = convert_request_idx.find( boost::make_tuple( get_account_id( op.owner ), op.requestid ) ); BOOST_REQUIRE( convert_request != convert_request_idx.end() ); - BOOST_REQUIRE( convert_request->owner == op.owner ); - BOOST_REQUIRE( convert_request->requestid == op.requestid ); - BOOST_REQUIRE( convert_request->amount.amount.value == ASSET( "3.000 TBD" ).amount.value ); + BOOST_REQUIRE( convert_request->get_owner() == get_account_id( op.owner ) ); + BOOST_REQUIRE( convert_request->get_request_id() == op.requestid ); + BOOST_REQUIRE( convert_request->get_convert_amount().amount.value == ASSET( "3.000 TBD" ).amount.value ); //BOOST_REQUIRE( convert_request->premium == 100000 ); - BOOST_REQUIRE( convert_request->conversion_date == db->head_block_time() + HIVE_CONVERSION_DELAY ); + BOOST_REQUIRE( convert_request->get_conversion_date() == db->head_block_time() + HIVE_CONVERSION_DELAY ); validate_database(); } FC_LOG_AND_RETHROW() @@ -3150,6 +3209,434 @@ BOOST_AUTO_TEST_CASE( fixture_convert_checks_balance ) } +BOOST_AUTO_TEST_CASE( collateralized_convert_authorities ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: collateralized_convert_authorities" ); + + ACTORS( (alice)(bob) ) + + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "4.000 TESTS" ) ) ); + + fund( "alice", ASSET( "300.000 TBD" ) ); + fund( "alice", ASSET( "1000.000 TESTS" ) ); + + collateralized_convert_operation op; + op.owner = "alice"; + op.amount = ASSET( "200.000 TESTS" ); + + signed_transaction tx; + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + tx.operations.push_back( op ); + + BOOST_TEST_MESSAGE( "--- Test failure when no signatures" ); + HIVE_REQUIRE_THROW( db->push_transaction( tx, 0 ), tx_missing_active_auth ); + + BOOST_TEST_MESSAGE( "--- Test failure when signed by a signature not in the account's authority" ); + sign( tx, alice_post_key ); + HIVE_REQUIRE_THROW( db->push_transaction( tx, 0 ), tx_missing_active_auth ); + + BOOST_TEST_MESSAGE( "--- Test failure when duplicate signatures" ); + tx.signatures.clear(); + sign( tx, alice_private_key ); + sign( tx, alice_private_key ); + HIVE_REQUIRE_THROW( db->push_transaction( tx, 0 ), tx_duplicate_sig ); + + BOOST_TEST_MESSAGE( "--- Test failure when signed by an additional signature not in the creator's authority" ); + tx.signatures.clear(); + sign( tx, alice_private_key ); + sign( tx, bob_private_key ); + HIVE_REQUIRE_THROW( db->push_transaction( tx, 0 ), tx_irrelevant_sig ); + + BOOST_TEST_MESSAGE( "--- Test success with owner signature" ); + tx.signatures.clear(); + sign( tx, alice_private_key ); + db->push_transaction( tx, 0 ); + + validate_database(); + generate_block(); + + //remove price feed to check one unlikely scenario + db_plugin->debug_update( [=]( database& db ) + { + db.modify( db.get_feed_history(), [&]( feed_history_object& feed ) + { + feed.current_median_history = feed.market_median_history = + feed.current_min_history = feed.current_max_history = price(); + } ); + } ); + + BOOST_TEST_MESSAGE( "--- Test failure without price feed" ); + tx.signatures.clear(); + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + sign( tx, alice_private_key ); + HIVE_REQUIRE_ASSERT( db->push_transaction( tx, 0 ), "!fhistory.current_median_history.is_null()" ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( collateralized_convert_apply ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: collateralized_convert_apply" ); + ACTORS( (alice)(bob) ); + + generate_block(); + + const auto& dgpo = db->get_dynamic_global_properties(); + const auto& feed = db->get_feed_history(); + db->skip_price_feed_limit_check = false; + + price price_1_for_4 = price( ASSET( "1.000 TBD" ), ASSET( "4.000 TESTS" ) ); + set_price_feed( price_1_for_4 ); + BOOST_REQUIRE( feed.current_median_history == price_1_for_4 ); + + //prevent HBD interest from interfering with the test + flat_map< string, vector > props; + props[ "hbd_interest_rate" ] = fc::raw::pack_to_vector( 0 ); + set_witness_props( props ); + + fund( "alice", ASSET( "100.000 TBD" ) ); + fund( "alice", ASSET( "1000.000 TESTS" ) ); + fund( "bob", ASSET( "100.000 TBD" ) ); + + BOOST_TEST_MESSAGE( "--- Test failure sending non-HIVE collateral" ); + collateralized_convert_operation op; + op.owner = "alice"; + op.amount = ASSET( "5.000 TBD" ); + HIVE_REQUIRE_ASSERT( push_transaction( op, alice_private_key ), "is_asset_type( amount, HIVE_SYMBOL )" ); + transfer( "alice", db->get_treasury_name(), get_hbd_balance( "alice" ) ); + + BOOST_TEST_MESSAGE( "--- Test failure sending negative collateral" ); + op.amount = ASSET( "-5.000 TESTS" ); + HIVE_REQUIRE_ASSERT( push_transaction( op, alice_private_key ), "amount.amount > 0" ); + + BOOST_TEST_MESSAGE( "--- Test failure sending zero collateral" ); + op.amount = ASSET( "0.000 TESTS" ); + HIVE_REQUIRE_ASSERT( push_transaction( op, alice_private_key ), "amount.amount > 0" ); + + BOOST_TEST_MESSAGE( "--- Test failure sending too small collateral" ); + op.amount = ASSET( "0.009 TESTS" ); //0.004 TESTS for immediate conversion which would give 0.001 TBD if there was no extra 5% fee + HIVE_REQUIRE_ASSERT( push_transaction( op, alice_private_key ), "converted_amount.amount > 0" ); + + BOOST_TEST_MESSAGE( "--- Test failure sending collateral exceeding balance" ); + op.amount = ASSET( "1000.001 TESTS" ); + HIVE_REQUIRE_ASSERT( push_transaction( op, alice_private_key ), "available >= -delta" ); + + //give alice enough for further tests + transfer( HIVE_INIT_MINER_NAME, "alice", ASSET( "9000.000 TESTS" ) ); + auto alice_balance = get_balance( "alice" ); + BOOST_REQUIRE( alice_balance == ASSET( "10000.000 TESTS" ) ); + + BOOST_TEST_MESSAGE( "--- Test ok - conversion at 25 cents per HIVE, both initial and actual" ); + op.amount = ASSET( "1000.000 TESTS" ); + auto conversion_time = db->head_block_time(); + push_transaction( op, alice_private_key ); + + alice_balance -= ASSET( "1000.000 TESTS" ); + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "119.047 TBD" ) ); // 1000/2 collateral * 10/42 price with fee + transfer( "alice", db->get_treasury_name(), get_hbd_balance( "alice" ) ); + + generate_block(); + + BOOST_TEST_MESSAGE( "--- Test failure - conversion with duplicate id" ); + op.amount = ASSET( "1000.000 TESTS" ); + HIVE_REQUIRE_CHAINBASE_ASSERT( push_transaction( op, alice_private_key ), "could not insert object, most likely a uniqueness constraint was violated" ); + + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "0.000 TBD" ) ); + + generate_blocks( conversion_time + HIVE_COLLATERALIZED_CONVERSION_DELAY - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + generate_block(); //actual conversion + alice_balance += ASSET( "500.003 TESTS" ); //getting back excess collateral (1000 - 119.047 * 42/10) + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "0.000 TBD" ) ); //actual conversion does not give any more HBD + { + auto recent_ops = get_last_operations( 1 ); + auto convert_op = recent_ops.back().get< fill_collateralized_convert_request_operation >(); + BOOST_REQUIRE( convert_op.owner == "alice" ); + BOOST_REQUIRE( convert_op.requestid == 0 ); + BOOST_REQUIRE( convert_op.amount_in == ASSET( "499.997 TESTS" ) ); + BOOST_REQUIRE( convert_op.amount_out == ASSET( "119.047 TBD" ) ); + BOOST_REQUIRE( convert_op.excess_collateral == ASSET( "500.003 TESTS" ) ); + } + + BOOST_TEST_MESSAGE( "--- Test ok - conversion at 25 cents initial, 12.5 cents per HIVE actual" ); + op.amount = ASSET( "1000.000 TESTS" ); + auto conversion_2_time = db->head_block_time(); + push_transaction( op, alice_private_key ); + + alice_balance -= ASSET( "1000.000 TESTS" ); + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "119.047 TBD" ) ); // 1000/2 collateral * 10/42 price with fee + transfer( "alice", db->get_treasury_name(), get_hbd_balance( "alice" ) ); + + price price_1_for_8 = price( ASSET( "1.000 TBD" ), ASSET( "8.000 TESTS" ) ); + set_price_feed( price_1_for_8 ); + set_price_feed( price_1_for_8 ); //need to do it twice or median won't be the one required + BOOST_REQUIRE( feed.current_median_history == price_1_for_8 ); + + generate_blocks( conversion_2_time + HIVE_COLLATERALIZED_CONVERSION_DELAY - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + generate_block(); //actual conversion + alice_balance += ASSET( "0.006 TESTS" ); //almost no excess collateral (1000 - 119.047 * 84/10) + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "0.000 TBD" ) ); + + BOOST_TEST_MESSAGE( "--- Test ok - conversion at 12.5 cents initial, 5 cents per HIVE actual" ); + op.amount = ASSET( "1000.000 TESTS" ); + auto conversion_3_time = db->head_block_time(); + push_transaction( op, alice_private_key ); + + alice_balance -= ASSET( "1000.000 TESTS" ); + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "59.523 TBD" ) ); // 1000/2 collateral * 10/84 price with fee + //transfer( "alice", db->get_treasury_name(), get_hbd_balance( "alice" ) ); leave it this time on account + + price price_1_for_20 = price( ASSET( "1.000 TBD" ), ASSET( "20.000 TESTS" ) ); + set_price_feed( price_1_for_20 ); + set_price_feed( price_1_for_20 ); + set_price_feed( price_1_for_20 ); + set_price_feed( price_1_for_20 ); //four times required to override three previous records as median + BOOST_REQUIRE( feed.current_median_history == price_1_for_20 ); + + generate_blocks( conversion_3_time + HIVE_COLLATERALIZED_CONVERSION_DELAY - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + generate_block(); //actual conversion + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); //no excess collateral + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "59.523 TBD" ) ); //even though there was too little collateral we still don't try to take back produced HBD + { + auto recent_ops = get_last_operations( 2 ); + auto sys_warn_op = recent_ops.back().get< system_warning_operation >(); + BOOST_REQUIRE( sys_warn_op.message.compare( 0, 23, "Insufficient collateral" ) == 0 ); + } + transfer( "alice", db->get_treasury_name(), get_hbd_balance( "alice" ) ); + + BOOST_TEST_MESSAGE( "--- Setting amount of HBD in the system to the edge of hard limit" ); + { + fc::uint128_t amount( dgpo.get_current_supply().amount.value ); + uint16_t limit2 = 2 * dgpo.hbd_stop_percent + HIVE_1_BASIS_POINT; //there is rounding when percent is calculated, hence some strange correction + amount = ( amount * limit2 ) / ( 2 * HIVE_100_PERCENT - limit2 ); + auto new_hbd = asset( amount.to_uint64(), HIVE_SYMBOL ) * feed.current_median_history; + new_hbd -= dgpo.get_current_hbd_supply() - db->get_treasury().get_hbd_balance(); + fund( "alice", new_hbd, false ); + uint16_t percent = db->calculate_HBD_percent(); + BOOST_REQUIRE_EQUAL( percent, dgpo.hbd_stop_percent ); + } + + BOOST_TEST_MESSAGE( "--- Test failure on too many HBD in the system" ); + op.amount = ASSET( "0.042 TESTS" ); //minimal amount of collateral that gives nonzero HBD at current price + HIVE_REQUIRE_ASSERT( push_transaction( op, alice_private_key ), "percent_hbd <= dgpo.hbd_stop_percent" ); + + const auto& collateralized_convert_request_idx = db->get_index< collateralized_convert_request_index, by_owner >(); + BOOST_REQUIRE( collateralized_convert_request_idx.empty() ); + + //let's make some room for conversion (treasury HBD does not count) + transfer( "alice", db->get_treasury_name(), ASSET( "25.000 TBD" ) ); + + BOOST_TEST_MESSAGE( "--- Test ok - conversion at 5 cents initial, 5 cents per HIVE actual (while price is artificial)" ); + op.amount = ASSET( "1000.000 TESTS" ); + auto conversion_4_time = db->head_block_time(); + auto alice_hbd_balance = get_hbd_balance( "alice" ); + push_transaction( op, alice_private_key ); + + alice_balance -= ASSET( "1000.000 TESTS" ); + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + alice_hbd_balance += ASSET( "23.809 TBD" ); // 1000/2 collateral * 1/21 price with fee + BOOST_REQUIRE( get_hbd_balance( "alice" ) == alice_hbd_balance ); + + BOOST_TEST_MESSAGE( "--- Test ok - regular conversion at artificial price" ); + { + //let's schedule conversion from HBD to hive + convert_operation op; + op.owner = "bob"; + op.amount = ASSET( "20.000 TBD" ); + push_transaction( op, bob_private_key ); + } + generate_block(); + + //since we are nearly on the edge of HBD hard limit, let's use the opportunity and test what happens when we try to cross it + fund( "bob", dgpo.get_current_hbd_supply() ); + generate_blocks( HIVE_FEED_INTERVAL_BLOCKS - ( dgpo.head_block_number % HIVE_FEED_INTERVAL_BLOCKS ) ); + //last feed update should've put up artificial price of HIVE + auto recent_ops = get_last_operations( 2 ); + auto sys_warn_op = recent_ops.back().get< system_warning_operation >(); + BOOST_REQUIRE( sys_warn_op.message.compare( 0, 27, "HIVE price corrected upward" ) == 0 ); + + price price_1_for_10 = price( ASSET( "1.000 TBD" ), ASSET( "10.000 TESTS" ) ); + BOOST_REQUIRE( ( feed.current_median_history > price_1_for_10 ) && feed.current_median_history < price_1_for_8 ); + BOOST_REQUIRE( feed.current_max_history == price_1_for_4 ); //it is hard to force artificial correction of max price when it is so high to begin with + BOOST_REQUIRE( feed.market_median_history == price_1_for_20 ); //market driven median price should be intact + BOOST_REQUIRE( feed.current_min_history == price_1_for_20 ); //minimal price should be intact + + generate_blocks( conversion_4_time + HIVE_COLLATERALIZED_CONVERSION_DELAY - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_balance( "bob" ) == ASSET( "0.000 TESTS" ) ); + generate_block(); //actual conversion + alice_balance += ASSET( "500.011 TESTS" ); //excess collateral (1000 - 23.809 * 21/1) - alice used fee corrected market_median_price... + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_balance( "bob" ) == ASSET( "199.102 TESTS" ) ); //...but bob used ~1/10 price (artificial current_median_history) + + //put HBD on treasury where it does not count, but try to make some conversion with artificial price before it is changed back down + transfer( "alice", db->get_treasury_name(), get_hbd_balance( "alice" ) ); + transfer( "bob", db->get_treasury_name(), get_hbd_balance( "bob" ) ); + + BOOST_TEST_MESSAGE( "--- Test failed - conversion initiated while artificial price is active" ); + op.amount = ASSET( "1000.000 TESTS" ); + HIVE_REQUIRE_ASSERT( push_transaction( op, alice_private_key ), "dgpo.hbd_print_rate > 0" ); + + generate_blocks( HIVE_FEED_INTERVAL_BLOCKS - ( dgpo.head_block_number % HIVE_FEED_INTERVAL_BLOCKS ) ); + + //since HBD on treasury does not count the price should now be back to normal price + BOOST_REQUIRE( feed.current_median_history == price_1_for_20 ); + BOOST_REQUIRE( feed.market_median_history == price_1_for_20 ); + BOOST_REQUIRE( feed.current_min_history == price_1_for_20 ); + BOOST_REQUIRE( feed.current_max_history == price_1_for_4 ); + + BOOST_TEST_MESSAGE( "--- Test ok - conversion at 5 cents initial, 50 cents per HIVE actual" ); + op.amount = ASSET( "1000.000 TESTS" ); + auto conversion_5_time = db->head_block_time(); + push_transaction( op, alice_private_key ); + + alice_balance -= ASSET( "1000.000 TESTS" ); + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "23.809 TBD" ) ); // 1000/2 collateral * 1/21 price with fee + + price price_1_for_2 = price( ASSET( "1.000 TBD" ), ASSET( "2.000 TESTS" ) ); + set_price_feed( price_1_for_2 ); + set_price_feed( price_1_for_2 ); + set_price_feed( price_1_for_2 ); + set_price_feed( price_1_for_2 ); + set_price_feed( price_1_for_2 ); + set_price_feed( price_1_for_2 ); + set_price_feed( price_1_for_2 ); + set_price_feed( price_1_for_2 ); + set_price_feed( price_1_for_2 ); + BOOST_REQUIRE( feed.current_median_history == price_1_for_2 ); + + generate_blocks( conversion_5_time + HIVE_COLLATERALIZED_CONVERSION_DELAY - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + generate_block(); //actual conversion + alice_balance += ASSET( "950.002 TESTS" ); //a lot of excess collateral (1000 - 23.809 * 21/10) + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( collateralized_convert_narrow_price ) +{ + //test covers bug where fee was not applied, because price scaling code was truncating it + try + { + BOOST_TEST_MESSAGE( "Testing: collateralized_convert_narrow_price" ); + ACTORS( (alice) ); + + generate_block(); + + const auto& feed = db->get_feed_history(); + db->skip_price_feed_limit_check = false; + + price price_1_for_2 = price( ASSET( "0.001 TBD" ), ASSET( "0.002 TESTS" ) ); + set_price_feed( price_1_for_2 ); + BOOST_REQUIRE( feed.current_median_history == price_1_for_2 ); + + //prevent HBD interest from interfering with the test + flat_map< string, vector > props; + props[ "hbd_interest_rate" ] = fc::raw::pack_to_vector( 0 ); + set_witness_props( props ); + + fund( "alice", ASSET( "0.042 TESTS" ) ); + + BOOST_TEST_MESSAGE( "--- Test ok - conversion at 50 cents per HIVE, both initial and actual" ); + collateralized_convert_operation op; + op.owner = "alice"; + op.amount = ASSET( "0.042 TESTS" ); + auto conversion_time = db->head_block_time(); + push_transaction( op, alice_private_key ); + + BOOST_REQUIRE( get_balance( "alice" ) == ASSET( "0.000 TESTS" ) ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "0.010 TBD" ) ); // 0.042/2 collateral * 10/21 price with fee + + generate_blocks( conversion_time + HIVE_COLLATERALIZED_CONVERSION_DELAY - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + BOOST_REQUIRE( get_balance( "alice" ) == ASSET( "0.000 TESTS" ) ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "0.010 TBD" ) ); + generate_block(); //actual conversion + BOOST_REQUIRE( get_balance( "alice" ) == ASSET( "0.021 TESTS" ) ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "0.010 TBD" ) ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( collateralized_convert_wide_price ) +{ + //test covers potential bug where application of fee to price would exceed 64bit + try + { + BOOST_TEST_MESSAGE( "Testing: collateralized_convert_wide_price" ); + ACTORS( ( alice ) ); + + generate_block(); + + const auto& feed = db->get_feed_history(); + db->skip_price_feed_limit_check = false; + + price price_1_for_2 = price( ASSET( "4611686018427387.000 TBD" ), ASSET( "9223372036854774.000 TESTS" ) ); + set_price_feed( price_1_for_2 ); + BOOST_REQUIRE( feed.current_median_history == price_1_for_2 ); + + //prevent HBD interest from interfering with the test + flat_map< string, vector > props; + props[ "hbd_interest_rate" ] = fc::raw::pack_to_vector( 0 ); + set_witness_props( props ); + + fund( "alice", ASSET( "50000000.000 TESTS" ) ); + + BOOST_TEST_MESSAGE( "--- Test failure on too many HBD in the system" ); + collateralized_convert_operation op; + op.owner = "alice"; + op.amount = ASSET( "42000000.000 TESTS" ); + HIVE_REQUIRE_ASSERT( push_transaction( op, alice_private_key ), "percent_hbd <= dgpo.hbd_stop_percent" ); + + BOOST_TEST_MESSAGE( "--- Test ok - conversion at 50 cents per HIVE, both initial and actual" ); + op.amount = ASSET( "4200000.000 TESTS" ); + auto conversion_time = db->head_block_time(); + auto alice_balance = get_balance( "alice" ); + push_transaction( op, alice_private_key ); + alice_balance -= ASSET( "4200000.000 TESTS" ); + + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "1000000.000 TBD" ) ); + + generate_blocks( conversion_time + HIVE_COLLATERALIZED_CONVERSION_DELAY - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "1000000.000 TBD" ) ); + generate_block(); //actual conversion + alice_balance += ASSET( "2100000.000 TESTS" ); + BOOST_REQUIRE( get_balance( "alice" ) == alice_balance ); + BOOST_REQUIRE( get_hbd_balance( "alice" ) == ASSET( "1000000.000 TBD" ) ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_CASE( limit_order_create_validate ) { try @@ -4267,10 +4754,10 @@ BOOST_AUTO_TEST_CASE( account_recovery ) const auto& request_idx = db->get_index< account_recovery_request_index >().indices(); auto req_itr = request_idx.begin(); - BOOST_REQUIRE( req_itr->account_to_recover == "bob" ); - BOOST_REQUIRE( req_itr->new_owner_authority == authority( 1, generate_private_key( "expire" ).get_public_key(), 1 ) ); - BOOST_REQUIRE( req_itr->expires == db->head_block_time() + HIVE_ACCOUNT_RECOVERY_REQUEST_EXPIRATION_PERIOD ); - auto expires = req_itr->expires; + BOOST_REQUIRE( req_itr->get_account_to_recover() == "bob" ); + BOOST_REQUIRE( req_itr->get_new_owner_authority() == authority( 1, generate_private_key( "expire" ).get_public_key(), 1 ) ); + auto expires = req_itr->get_expiration_time(); + BOOST_REQUIRE( expires == db->head_block_time() + HIVE_ACCOUNT_RECOVERY_REQUEST_EXPIRATION_PERIOD ); ++req_itr; BOOST_REQUIRE( req_itr == request_idx.end() ); @@ -8213,7 +8700,7 @@ BOOST_AUTO_TEST_CASE( claim_account_apply ) block_id_type hbid = db->head_block_id(); optional block = db->fetch_block_by_id(hbid); BOOST_REQUIRE( block.valid() ); - BOOST_CHECK_EQUAL( block->transactions.size(), 1 ); + BOOST_CHECK_EQUAL( block->transactions.size(), 1u ); BOOST_CHECK( db->get_account( "alice" ).pending_claimed_accounts == 3 ); int64_t new_value = prev_c_subs - HIVE_ACCOUNT_SUBSIDY_PRECISION; // Usage applied before decay @@ -8423,8 +8910,8 @@ BOOST_AUTO_TEST_CASE( create_claimed_account_apply ) const auto& bob_meta = db->get< account_metadata_object, by_account >( bob.get_id() ); BOOST_REQUIRE( bob_meta.json_metadata == "{\"foo\":\"bar\"}" ); #endif - BOOST_REQUIRE( bob.proxy == "" ); - BOOST_REQUIRE( bob.recovery_account == "alice" ); + CHECK_NO_PROXY( bob ); + BOOST_REQUIRE( bob.get_recovery_account() == "alice" ); BOOST_REQUIRE( bob.created == db->head_block_time() ); BOOST_REQUIRE( bob.get_balance().amount.value == ASSET( "0.000 TESTS" ).amount.value ); BOOST_REQUIRE( bob.get_hbd_balance().amount.value == ASSET( "0.000 TBD" ).amount.value ); @@ -8459,7 +8946,7 @@ BOOST_AUTO_TEST_CASE( create_claimed_account_apply ) tx.operations.push_back( op ); db->push_transaction( tx, 0 ); - BOOST_REQUIRE( db->get_account( "charlie" ).recovery_account == account_name_type() ); + BOOST_REQUIRE( !db->get_account( "charlie" ).has_recovery_account() ); validate_database(); } FC_LOG_AND_RETHROW() @@ -8917,5 +9404,421 @@ BOOST_AUTO_TEST_CASE( account_update2_apply ) } FC_LOG_AND_RETHROW() } + +BOOST_AUTO_TEST_CASE( recurrent_transfer_validate ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: recurrent_transfer_validate" ); + + recurrent_transfer_operation op; + op.from = "alice"; + op.to = "bob"; + op.memo = "Memo"; + op.recurrence = 24; + op.executions = 10; + op.amount = asset( 100, HIVE_SYMBOL ); + op.validate(); + + BOOST_TEST_MESSAGE( " --- Invalid from account" ); + op.from = "alice-"; + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.from = "alice"; + + BOOST_TEST_MESSAGE( " --- Invalid to account" ); + op.to = "bob-"; + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.to = "bob"; + + BOOST_TEST_MESSAGE( " --- Memo too long" ); + std::string memo; + for ( int i = 0; i < HIVE_MAX_MEMO_SIZE + 1; i++ ) + memo += "x"; + op.memo = memo; + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.memo = "Memo"; + + BOOST_TEST_MESSAGE( " --- Negative amount" ); + op.amount = -op.amount; + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.amount = -op.amount; + + BOOST_TEST_MESSAGE( " --- Transferring vests" ); + op.amount = asset( 100, VESTS_SYMBOL ); + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.amount = asset( 100, HIVE_SYMBOL ); + + BOOST_TEST_MESSAGE( " --- Recurrence too low" ); + op.recurrence = 3; + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.recurrence = 24; + + BOOST_TEST_MESSAGE( " --- Transfer to yourself" ); + op.from = "bob"; + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.from = "alice"; + + BOOST_TEST_MESSAGE( " --- recurrence * executions is too high with recurrence being every day" ); + op.executions = HIVE_MAX_RECURRENT_TRANSFER_END_DATE + 1; // one day too many + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + + BOOST_TEST_MESSAGE( " --- recurrence * executions is too high with recurrence being every two days" ); + op.recurrence = 48; + op.executions = HIVE_MAX_RECURRENT_TRANSFER_END_DATE / 2 + 1; // one day too many + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.executions = 10; + + BOOST_TEST_MESSAGE( " --- executions is less than 2" ); + op.executions = 1; + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + op.executions = 0; + HIVE_REQUIRE_THROW( op.validate(), fc::assert_exception ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( recurrent_transfer_apply ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: recurrent_transfer_apply" ); + + ACTORS( (alice)(bob) ) + generate_block(); + + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + + fund( "alice", 10000 ); + fund( "alice", ASSET("100.000 TBD") ); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "10.000 TESTS" ).amount.value ); + + recurrent_transfer_operation op; + op.from = "alice"; + op.to = "bob"; + op.memo = "test"; + op.amount = ASSET( "5.000 TESTS" ); + op.recurrence = 72; + op.executions = 10; + + BOOST_TEST_MESSAGE( "--- Test normal transaction" ); + push_transaction(op, alice_private_key); + + const auto execution_block_time = db->head_block_time(); + const auto& recurrent_transfer_pre_execution = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) ); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "10.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "0.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 1 ); + BOOST_REQUIRE( recurrent_transfer_pre_execution->get_trigger_date() == execution_block_time); + BOOST_REQUIRE( recurrent_transfer_pre_execution->recurrence == 72 ); + BOOST_REQUIRE( recurrent_transfer_pre_execution->amount == ASSET( "5.000 TESTS" ) ); + BOOST_REQUIRE( recurrent_transfer_pre_execution->remaining_executions == 10 ); + BOOST_REQUIRE( recurrent_transfer_pre_execution->memo == "test" ); + validate_database(); + + generate_block(); + BOOST_TEST_MESSAGE( "--- test initial recurrent transfer execution" ); + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + validate_database(); + + BOOST_TEST_MESSAGE( "--- test recurrent transfer trigger date and remaining executions post genesis execution" ); + const auto& recurrent_transfer = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) ); + + BOOST_REQUIRE( recurrent_transfer->get_trigger_date() == execution_block_time + fc::hours(op.recurrence) ); + BOOST_REQUIRE( recurrent_transfer->remaining_executions == 9 ); + + BOOST_TEST_MESSAGE( "--- test updating the recurrent transfer parameters" ); + op.memo = "test_updated"; + op.amount = ASSET( "2.000 TESTS" ); + op.executions = 20; + push_transaction(op, alice_private_key); + + const auto recurrent_transfer_new = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) )->copy_chain_object(); + + BOOST_REQUIRE( recurrent_transfer_new.get_trigger_date() == recurrent_transfer->get_trigger_date()); + BOOST_REQUIRE( recurrent_transfer_new.recurrence == 72 ); + BOOST_REQUIRE( recurrent_transfer_new.amount == ASSET( "2.000 TESTS" ) ); + BOOST_REQUIRE( recurrent_transfer_new.remaining_executions == 20 ); + BOOST_REQUIRE( recurrent_transfer_new.memo == "test_updated" ); + validate_database(); + + generate_block(); + + BOOST_TEST_MESSAGE( "--- test setting the amount to HBD" ); + + op.amount = ASSET("10.000 TBD"); + push_transaction(op, alice_private_key); + + BOOST_REQUIRE( recurrent_transfer->amount == ASSET("10.000 TBD") ); + validate_database(); + + generate_block(); + BOOST_TEST_MESSAGE( "--- test updating the recurrence" ); + + op.recurrence = 96; + push_transaction(op, alice_private_key); + + BOOST_REQUIRE( recurrent_transfer->get_trigger_date() == db->head_block_time() + fc::hours(op.recurrence) ); + BOOST_REQUIRE( recurrent_transfer->recurrence == 96 ); + BOOST_REQUIRE( recurrent_transfer->get_trigger_date() != recurrent_transfer_new.get_trigger_date() ); + BOOST_REQUIRE( recurrent_transfer->remaining_executions == 20 ); + validate_database(); + + generate_block(); + BOOST_TEST_MESSAGE( "--- test deleting the recurrent transfer" ); + + op.amount = ASSET("0.000 TESTS"); + push_transaction(op, alice_private_key); + + const auto* deleted_recurrent_transfer = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) ); + BOOST_REQUIRE( deleted_recurrent_transfer == nullptr ); + + generate_block(); + op.amount = ASSET("50.000 TESTS"); + + validate_database(); + + BOOST_TEST_MESSAGE( "--- test not enough tokens for the first recurrent transfer" ); + + op.amount = ASSET("500000.000 TBD"); + + HIVE_REQUIRE_THROW( push_transaction(op, alice_private_key), fc::exception ); + validate_database(); + + BOOST_TEST_MESSAGE( "--- test trying to delete a non existing transfer" ); + + op.amount = ASSET( "0.000 TBD" ); + HIVE_REQUIRE_THROW( push_transaction(op, alice_private_key), fc::exception ); + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( recurrent_transfer_hbd ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: recurrent_transfer with HBD" ); + + ACTORS( (alice)(bob) ) + generate_block(); + + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + + fund( "alice", ASSET("100.000 TBD") ); + + recurrent_transfer_operation op; + op.from = "alice"; + op.to = "bob"; + op.memo = "test"; + op.amount = ASSET( "10.000 TBD" ); + op.recurrence = 72; + op.executions = 5; + push_transaction(op, alice_private_key); + + const auto execution_block_time = db->head_block_time(); + BOOST_REQUIRE( get_hbd_balance( "alice" ).amount.value == ASSET( "100.000 TBD" ).amount.value ); + BOOST_REQUIRE( get_hbd_balance( "bob" ).amount.value == ASSET( "0.000 TBD" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 1 ); + validate_database(); + generate_block(); + + BOOST_TEST_MESSAGE( "--- test initial recurrent transfer execution" ); + BOOST_REQUIRE( get_hbd_balance( "alice" ).amount.value == ASSET( "90.000 TBD" ).amount.value ); + BOOST_REQUIRE( get_hbd_balance( "bob" ).amount.value == ASSET( "10.000 TBD" ).amount.value ); + validate_database(); + + BOOST_TEST_MESSAGE( "--- test recurrent transfer trigger date post genesis execution" ); + const auto& recurrent_transfer = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) ); + + BOOST_REQUIRE( recurrent_transfer->get_trigger_date() == execution_block_time + fc::hours(op.recurrence) ); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( recurrent_transfer_max_open_transfers ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: too many open recurrent transfers" ); + ACTORS( (alice)(bob) ) + + generate_block(); + + #define CREATE_ACTORS(z, n, text) ACTORS( (actor ## n) ); + BOOST_PP_REPEAT(HIVE_MAX_OPEN_RECURRENT_TRANSFERS, CREATE_ACTORS, ) + generate_block(); + + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + + fund( "alice", ASSET("10000.000 TESTS") ); + + recurrent_transfer_operation op; + op.from = "alice"; + op.memo = "test"; + op.amount = ASSET( "5.000 TESTS" ); + op.recurrence = 72; + op.executions = 13; + + for (int i = 0; i < HIVE_MAX_OPEN_RECURRENT_TRANSFERS; i++) { + op.to = "actor" + std::to_string(i); + push_transaction(op, alice_private_key); + } + + BOOST_TEST_MESSAGE( "Testing: executing all the recurrent transfers"); + generate_block(); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "8725.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor0" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor123" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor254" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == HIVE_MAX_OPEN_RECURRENT_TRANSFERS); + + BOOST_TEST_MESSAGE( "Testing: Cannot create more than HIVE_MAX_OPEN_RECURRENT_TRANSFERS transfers"); + op.to = "bob"; + HIVE_REQUIRE_ASSERT( push_transaction(op, alice_private_key), "from_account.open_recurrent_transfers < HIVE_MAX_OPEN_RECURRENT_TRANSFERS" ); + + BOOST_TEST_MESSAGE( "Testing: Can still edit existing transfer even when at HIVE_MAX_OPEN_RECURRENT_TRANSFERS transfers" ); + op.to = "actor123"; + op.memo = "edit"; + op.amount = ASSET( "15.000 TESTS" ); + op.recurrence = 24; + op.executions = 3; + auto edit_time = db->head_block_time(); + push_transaction( op, alice_private_key ); + generate_block(); + + // since edit does not trigger transfer right away (unless it was scheduled for that block) + // there should be no immediate change in balance + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "8725.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor123" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == HIVE_MAX_OPEN_RECURRENT_TRANSFERS ); + + generate_blocks( edit_time + fc::hours( 24 ), false ); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "8710.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor123" ).amount.value == ASSET( "20.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == HIVE_MAX_OPEN_RECURRENT_TRANSFERS ); + + BOOST_TEST_MESSAGE( "Testing: Can still remove existing transfer even when at HIVE_MAX_OPEN_RECURRENT_TRANSFERS transfers" ); + op.to = "actor123"; + op.memo = "erase"; + op.amount = ASSET( "0.000 TESTS" ); + push_transaction( op, alice_private_key ); + generate_block(); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "8710.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor123" ).amount.value == ASSET( "20.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == HIVE_MAX_OPEN_RECURRENT_TRANSFERS - 1 ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + + +BOOST_AUTO_TEST_CASE( recurrent_transfer_max_transfer_processed_per_block ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: too many open recurrent transfers" ); + ACTORS( (alice)(bob)(eve)(martin) ) + generate_block(); + + #define CREATE_ACTORS(z, n, text) ACTORS( (actor ## n) ); + BOOST_PP_REPEAT(251, CREATE_ACTORS, ) + + generate_block(); + const char *senders[4] = { "alice", "bob", "eve", "martin" }; + const fc::ecc::private_key senders_keys[4] = { alice_private_key, bob_private_key, eve_private_key, martin_private_key }; + + fund( "alice", ASSET("1000.000 TESTS") ); + fund( "bob", ASSET("1000.000 TESTS") ); + fund( "eve", ASSET("1000.000 TESTS") ); + fund( "martin", ASSET("1000.000 TESTS") ); + + recurrent_transfer_operation op; + op.memo = "test"; + op.amount = ASSET( "1.000 TESTS" ); + op.recurrence = 24; + op.executions = 25; + + // 1000 recurrent transfers + for (int k = 0; k < 4; k++) { + op.from = senders[k]; + for (int i = 0; i < 250; i++) { + op.to = "actor" + std::to_string(i); + push_transaction(op, senders_keys[k]); + } + } + + // Those transfers won't be executed on the first block but on the second + for (int k = 0; k < 4; k++) { + op.from = senders[k]; + op.to = "actor250"; + op.amount = ASSET( "3.000 TESTS" ); + push_transaction(op, senders_keys[k]); + } + + BOOST_TEST_MESSAGE( "Testing: executing the first 1000 recurrent transfers"); + const auto creation_block_time = db->head_block_time(); + generate_block(); + + const auto recurrent_transfer_no_drift_before = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, actor0_id) )->copy_chain_object(); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "750.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor0" ).amount.value == ASSET( "4.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor123" ).amount.value == ASSET( "4.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor250" ).amount.value == ASSET( "0.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 251); + BOOST_REQUIRE( recurrent_transfer_no_drift_before.get_trigger_date() == creation_block_time + fc::days(1)); + + BOOST_TEST_MESSAGE( "Executing the remaining 4 recurrent payments"); + + generate_block(); + + const auto recurrent_transfer_drift_before = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, actor250_id) )->copy_chain_object(); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "747.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor0" ).amount.value == ASSET( "4.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor123" ).amount.value == ASSET( "4.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor250" ).amount.value == ASSET( "12.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 251); + BOOST_REQUIRE( recurrent_transfer_drift_before.get_trigger_date() == creation_block_time + fc::days(1)); + validate_database(); + + auto blocks_until_next_execution = fc::days(1).to_seconds() / HIVE_BLOCK_INTERVAL - 2; + ilog("generating ${blocks} blocks", ("blocks", blocks_until_next_execution)); + generate_blocks( blocks_until_next_execution ); + + BOOST_TEST_MESSAGE( "Testing: executing the first 1000 recurrent transfers for the second time"); + + const auto recurrent_transfer_no_drift_after = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, actor0_id) )->copy_chain_object(); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "497.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor0" ).amount.value == ASSET( "8.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor123" ).amount.value == ASSET( "8.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor250" ).amount.value == ASSET( "12.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 251); + BOOST_REQUIRE( recurrent_transfer_no_drift_after.get_trigger_date() == recurrent_transfer_no_drift_before.get_trigger_date() + fc::hours(recurrent_transfer_no_drift_before.recurrence)); + + BOOST_TEST_MESSAGE( "Executing the remaining 4 recurrent payments for the second time"); + generate_block(); + + const auto recurrent_transfer_drift_after = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, actor250_id) )->copy_chain_object(); + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "494.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor0" ).amount.value == ASSET( "8.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor123" ).amount.value == ASSET( "8.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "actor250" ).amount.value == ASSET( "24.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 251); + BOOST_REQUIRE( recurrent_transfer_drift_after.get_trigger_date() == recurrent_transfer_drift_before.get_trigger_date() + fc::hours(recurrent_transfer_drift_before.recurrence)); + validate_database(); + + } + FC_LOG_AND_RETHROW() +} + BOOST_AUTO_TEST_SUITE_END() #endif diff --git a/tests/unit/tests/operation_time_tests.cpp b/tests/unit/tests/operation_time_tests.cpp index 2f10111e05..f6e43179c3 100644 --- a/tests/unit/tests/operation_time_tests.cpp +++ b/tests/unit/tests/operation_time_tests.cpp @@ -1192,7 +1192,7 @@ OOST_AUTO_TEST_CASE( nested_comments ) */ -BOOST_AUTO_TEST_CASE( vesting_withdrawals ) +BOOST_AUTO_TEST_CASE( vesting_withdrawals, *boost::unit_test::disabled() ) { try { @@ -1219,7 +1219,7 @@ BOOST_AUTO_TEST_CASE( vesting_withdrawals ) asset withdraw_rate = new_alice.vesting_withdraw_rate; BOOST_TEST_MESSAGE( "Generating block up to first withdrawal" ); - generate_blocks( next_withdrawal - ( HIVE_BLOCK_INTERVAL / 2 ), true); + generate_blocks( next_withdrawal - HIVE_BLOCK_INTERVAL ); BOOST_REQUIRE( get_vesting( "alice" ).amount.value == vesting_shares.amount.value ); @@ -1464,8 +1464,11 @@ BOOST_AUTO_TEST_CASE( feed_publish_mean ) BOOST_TEST_MESSAGE( "Get feed history object" ); auto& feed_history = db->get_feed_history(); BOOST_TEST_MESSAGE( "Check state" ); - BOOST_REQUIRE( feed_history.current_median_history == price( asset( 1000, HBD_SYMBOL ), asset( 99000, HIVE_SYMBOL) ) ); - BOOST_REQUIRE( feed_history.price_history[ 0 ] == price( asset( 1000, HBD_SYMBOL ), asset( 99000, HIVE_SYMBOL) ) ); + { + auto expected_price = price( asset( 1000, HBD_SYMBOL ), asset( 99000, HIVE_SYMBOL ) ); + BOOST_REQUIRE( feed_history.current_median_history == expected_price ); + BOOST_REQUIRE( feed_history.price_history[ 0 ] == expected_price ); + } validate_database(); for ( int i = 0; i < 23; i++ ) @@ -1530,8 +1533,8 @@ BOOST_AUTO_TEST_CASE( convert_delay ) BOOST_TEST_MESSAGE( "Verify conversion is not applied" ); const auto& alice_2 = db->get_account( "alice" ); - const auto& convert_request_idx = db->get_index< convert_request_index >().indices().get< by_owner >(); - auto convert_request = convert_request_idx.find( boost::make_tuple( "alice", 2 ) ); + const auto& convert_request_idx = db->get_index< convert_request_index, by_owner >(); + auto convert_request = convert_request_idx.find( boost::make_tuple( alice_2.get_id(), 2 ) ); BOOST_REQUIRE( convert_request != convert_request_idx.end() ); BOOST_REQUIRE( alice_2.get_balance().amount.value == 0 ); @@ -1545,7 +1548,7 @@ BOOST_AUTO_TEST_CASE( convert_delay ) const auto& alice_3 = db->get_account( "alice" ); auto vop = get_last_operations( 1 )[0].get< fill_convert_request_operation >(); - convert_request = convert_request_idx.find( boost::make_tuple( "alice", 2 ) ); + convert_request = convert_request_idx.find( boost::make_tuple( alice_3.get_id(), 2 ) ); BOOST_REQUIRE( convert_request == convert_request_idx.end() ); BOOST_REQUIRE( alice_3.get_balance().amount.value == 2500 ); BOOST_REQUIRE( alice_3.get_hbd_balance().amount.value == ( start_balance - op.amount ).amount.value ); @@ -1769,12 +1772,21 @@ BOOST_AUTO_TEST_CASE( hbd_interest ) db->push_transaction( tx, 0 ); auto& gpo = db->get_dynamic_global_properties(); - auto interest_op = get_last_operations( 1 )[0].get< interest_operation >(); + BOOST_REQUIRE(gpo.get_hbd_interest_rate() > 0); + + if(db->has_hardfork(HIVE_HARDFORK_1_25)) + { + /// After HF 25 only HBD held on savings should get interest + BOOST_REQUIRE(get_hbd_balance("alice").amount.value == alice_hbd.amount.value - ASSET("1.000 TBD").amount.value); + } + else + { + auto interest_op = get_last_operations( 1 )[0].get< interest_operation >(); + BOOST_REQUIRE( static_cast(get_hbd_balance( "alice" ).amount.value) == alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value + ( ( ( ( uint128_t( alice_hbd.amount.value ) * ( db->head_block_time() - start_time ).to_seconds() ) / HIVE_SECONDS_PER_YEAR ) * gpo.get_hbd_interest_rate() ) / HIVE_100_PERCENT ).to_uint64() ); + BOOST_REQUIRE( interest_op.owner == "alice" ); + BOOST_REQUIRE( interest_op.interest.amount.value == get_hbd_balance( "alice" ).amount.value - ( alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value ) ); + } - BOOST_REQUIRE( gpo.get_hbd_interest_rate() > 0 ); - BOOST_REQUIRE( static_cast(get_hbd_balance( "alice" ).amount.value) == alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value + ( ( ( ( uint128_t( alice_hbd.amount.value ) * ( db->head_block_time() - start_time ).to_seconds() ) / HIVE_SECONDS_PER_YEAR ) * gpo.get_hbd_interest_rate() ) / HIVE_100_PERCENT ).to_uint64() ); - BOOST_REQUIRE( interest_op.owner == "alice" ); - BOOST_REQUIRE( interest_op.interest.amount.value == get_hbd_balance( "alice" ).amount.value - ( alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value ) ); validate_database(); BOOST_TEST_MESSAGE( "Testing interest under interest period" ); @@ -1809,7 +1821,109 @@ BOOST_AUTO_TEST_CASE( hbd_interest ) sign( tx, alice_private_key ); db->push_transaction( tx, 0 ); - BOOST_REQUIRE( static_cast(get_hbd_balance( "alice" ).amount.value) == alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value + ( ( ( ( uint128_t( alice_hbd.amount.value ) * ( db->head_block_time() - start_time ).to_seconds() + alice_coindays ) / HIVE_SECONDS_PER_YEAR ) * gpo.get_hbd_interest_rate() ) / HIVE_100_PERCENT ).to_uint64() ); + if(db->has_hardfork(HIVE_HARDFORK_1_25)) + { + /// After HF 25 only HBD held on savings should get interest + BOOST_REQUIRE(get_hbd_balance("alice").amount.value == alice_hbd.amount.value - ASSET("1.000 TBD").amount.value); + } + else + { + BOOST_REQUIRE( static_cast(get_hbd_balance( "alice" ).amount.value) == alice_hbd.amount.value - ASSET( "1.000 TBD" ).amount.value + ( ( ( ( uint128_t( alice_hbd.amount.value ) * ( db->head_block_time() - start_time ).to_seconds() + alice_coindays ) / HIVE_SECONDS_PER_YEAR ) * gpo.get_hbd_interest_rate() ) / HIVE_100_PERCENT ).to_uint64() ); + } + + validate_database(); + } + FC_LOG_AND_RETHROW(); +} + +BOOST_AUTO_TEST_CASE(hbd_savings_interest) +{ + using hive::plugins::condenser_api::legacy_asset; + + try + { + ACTORS((alice)) + generate_block(); + vest(HIVE_INIT_MINER_NAME, "alice", ASSET("10.000 TESTS")); + + set_price_feed(price(ASSET("1.000 TBD"), ASSET("1.000 TESTS"))); + + BOOST_TEST_MESSAGE("Testing savings interest over smallest interest period"); + + auto alice_funds = ASSET("31.903 TBD"); + fund("alice", alice_funds); + + transfer_to_savings_operation savings_supply; + savings_supply.from = "alice"; + savings_supply.to = "alice"; + savings_supply.memo = "New Tesla fund"; + savings_supply.amount = alice_funds; + push_transaction(savings_supply, alice_private_key); + + fund("alice", ASSET("3.000 TBD")); + + auto start_time = db->get_account("alice").savings_hbd_seconds_last_update; + auto alice_hbd = get_hbd_balance("alice"); + + BOOST_REQUIRE(alice_hbd == ASSET("3.000 TBD")); + + auto alice_hbd_savings = get_hbd_savings("alice"); + BOOST_REQUIRE(alice_hbd_savings == alice_funds); + + generate_blocks(db->head_block_time() + fc::seconds(HIVE_HBD_INTEREST_COMPOUND_INTERVAL_SEC), true); + + BOOST_REQUIRE(get_hbd_balance("alice") == ASSET("3.000 TBD")); + + transfer_to_savings_operation transfer; + transfer.to = "alice"; + transfer.from = "alice"; + transfer.memo = "Interest trigger"; + transfer.amount = ASSET("1.000 TBD"); + + /// This op is needed to trigger interest payment... + push_transaction(transfer, alice_private_key); + + auto& gpo = db->get_dynamic_global_properties(); + BOOST_REQUIRE(gpo.get_hbd_interest_rate() > 0); + + BOOST_TEST_MESSAGE("Alice HDB saving balance: " + legacy_asset::from_asset(get_hbd_savings("alice")).to_string()); + + auto interest_op = get_last_operations(1)[0].get< interest_operation >(); + BOOST_REQUIRE(static_cast(get_hbd_savings("alice").amount.value) == alice_hbd_savings.amount.value + ASSET("1.000 TBD").amount.value + ((((uint128_t(alice_hbd_savings.amount.value) * (db->head_block_time() - start_time).to_seconds()) / HIVE_SECONDS_PER_YEAR) * gpo.get_hbd_interest_rate()) / HIVE_100_PERCENT).to_uint64()); + BOOST_REQUIRE(interest_op.owner == "alice"); + BOOST_REQUIRE(interest_op.interest == get_hbd_savings("alice") - (alice_hbd_savings + ASSET("1.000 TBD"))); + + BOOST_TEST_MESSAGE("Alice got HDB saving interests: " + legacy_asset::from_asset(interest_op.interest).to_string()); + + validate_database(); + + BOOST_TEST_MESSAGE("Testing savings interest under interest period"); + + start_time = db->get_account("alice").savings_hbd_seconds_last_update; + alice_hbd_savings = get_hbd_savings("alice"); + + generate_blocks(db->head_block_time() + fc::seconds(HIVE_HBD_INTEREST_COMPOUND_INTERVAL_SEC / 2), true); + + /// This op is needed to trigger interest payment... + push_transaction(transfer, alice_private_key); + + BOOST_REQUIRE(get_hbd_savings("alice") == alice_hbd_savings + ASSET("1.000 TBD")); + + validate_database(); + + auto alice_coindays = uint128_t(alice_hbd_savings.amount.value) * (db->head_block_time() - start_time).to_seconds(); + alice_hbd_savings = get_hbd_savings("alice"); + start_time = db->get_account("alice").savings_hbd_seconds_last_update; + + BOOST_TEST_MESSAGE("Testing savings interest for longer period"); + + generate_blocks(db->head_block_time() + fc::seconds((HIVE_HBD_INTEREST_COMPOUND_INTERVAL_SEC * 7) / 3), true); + + /// This op is needed to trigger interest payment... + push_transaction(transfer, alice_private_key); + + BOOST_REQUIRE(static_cast(get_hbd_savings("alice").amount.value) == alice_hbd_savings.amount.value + ASSET("1.000 TBD").amount.value + ((((uint128_t(alice_hbd_savings.amount.value) * (db->head_block_time() - start_time).to_seconds() + alice_coindays) / HIVE_SECONDS_PER_YEAR) * gpo.get_hbd_interest_rate()) / HIVE_100_PERCENT).to_uint64()); + validate_database(); } FC_LOG_AND_RETHROW(); @@ -2597,7 +2711,8 @@ BOOST_AUTO_TEST_CASE( comment_freeze ) tx.operations.push_back( vote ); tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); sign( tx, sam_private_key ); - HIVE_REQUIRE_THROW( db->push_transaction( tx, 0 ), fc::assert_exception ); + /// Starting from HF25 voting for already paid posts is allowed again. + db->push_transaction( tx, 0 ); { const comment_object& _comment = db->get_comment( "alice", string( "test" ) ); @@ -2614,7 +2729,8 @@ BOOST_AUTO_TEST_CASE( comment_freeze ) tx.operations.push_back( vote ); tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); sign( tx, bob_private_key ); - HIVE_REQUIRE_THROW( db->push_transaction( tx, 0 ), fc::assert_exception ); + /// Starting from HF25 voting for already paid posts is allowed again. + db->push_transaction( tx, 0 ); { const comment_object& _comment = db->get_comment( "alice", string( "test" ) ); @@ -2631,8 +2747,8 @@ BOOST_AUTO_TEST_CASE( comment_freeze ) tx.operations.push_back( vote ); tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); sign( tx, dave_private_key ); - - HIVE_REQUIRE_THROW( db->push_transaction( tx, 0 ), fc::assert_exception ); + /// Starting from HF25 voting for already paid posts is allowed again. + db->push_transaction( tx, 0 ); { const comment_object& _comment = db->get_comment( "alice", string( "test" ) ); @@ -2843,9 +2959,19 @@ BOOST_AUTO_TEST_CASE( hbd_price_feed_limit ) const auto& gpo = db->get_dynamic_global_properties(); auto new_exchange_rate = price( gpo.get_current_hbd_supply(), asset( ( HIVE_100_PERCENT ) * gpo.get_current_supply().amount, HIVE_SYMBOL ) ); set_price_feed( new_exchange_rate ); - set_price_feed( new_exchange_rate ); + set_price_feed( new_exchange_rate, true ); + + auto recent_ops = get_last_operations( 2 ); + auto sys_warn_op = recent_ops.back().get< system_warning_operation >(); + BOOST_REQUIRE( sys_warn_op.message.compare( 0, 27, "HIVE price corrected upward" ) == 0 ); + + const auto& feed = db->get_feed_history(); + BOOST_REQUIRE( feed.current_median_history > new_exchange_rate && feed.current_median_history < exchange_rate ); + BOOST_REQUIRE( feed.market_median_history == new_exchange_rate ); + BOOST_REQUIRE( feed.current_min_history == new_exchange_rate ); + BOOST_REQUIRE( feed.current_max_history == exchange_rate ); - BOOST_REQUIRE( db->get_feed_history().current_median_history > new_exchange_rate && db->get_feed_history().current_median_history < exchange_rate ); + validate_database(); } FC_LOG_AND_RETHROW() } @@ -2986,7 +3112,7 @@ BOOST_AUTO_TEST_CASE( generate_account_subsidies ) }; const witness_schedule_object& wso = db->get_witness_schedule_object(); - BOOST_CHECK_EQUAL( wso.account_subsidy_rd.resource_unit, HIVE_ACCOUNT_SUBSIDY_PRECISION ); + BOOST_CHECK_EQUAL( wso.account_subsidy_rd.resource_unit, static_cast(HIVE_ACCOUNT_SUBSIDY_PRECISION) ); BOOST_CHECK_EQUAL( wso.account_subsidy_rd.budget_per_time_unit, 5123 ); BOOST_CHECK( is_pool_in_equilibrium( int64_t( wso.account_subsidy_rd.pool_eq ) , wso.account_subsidy_rd.budget_per_time_unit, wso.account_subsidy_rd.decay_params ) ); BOOST_CHECK( !is_pool_in_equilibrium( int64_t( wso.account_subsidy_rd.pool_eq )-1, wso.account_subsidy_rd.budget_per_time_unit, wso.account_subsidy_rd.decay_params ) ); @@ -3064,9 +3190,9 @@ BOOST_AUTO_TEST_CASE( account_subsidy_witness_limits ) generate_block(); // The transaction fails in generate_block(), meaning it is removed from the local node's transaction list - BOOST_CHECK_EQUAL( db->fetch_block_by_number( db->head_block_num() )->transactions.size(), 0 ); + BOOST_CHECK_EQUAL( db->fetch_block_by_number( db->head_block_num() )->transactions.size(), 0u ); BOOST_CHECK( db->get_account( "alice" ).pending_claimed_accounts == 0 ); - BOOST_CHECK_EQUAL( db->_pending_tx.size(), 0 ); + BOOST_CHECK_EQUAL( db->_pending_tx.size(), 0u ); } while( db->get< witness_object, by_name >( db->get_scheduled_witness( 1 ) ).schedule == witness_object::timeshare ); db->push_transaction( tx, 0 ); @@ -3074,9 +3200,9 @@ BOOST_AUTO_TEST_CASE( account_subsidy_witness_limits ) BOOST_CHECK( db->_pending_tx.size() == 1 ); // But generate another block, as a non-time-share witness, and it works generate_block(); - BOOST_CHECK_EQUAL( db->fetch_block_by_number( db->head_block_num() )->transactions.size(), 1 ); + BOOST_CHECK_EQUAL( db->fetch_block_by_number( db->head_block_num() )->transactions.size(), 1u ); BOOST_CHECK( db->get_account( "alice" ).pending_claimed_accounts == 1 ); - BOOST_CHECK_EQUAL( db->_pending_tx.size(), 0 ); + BOOST_CHECK_EQUAL( db->_pending_tx.size(), 0u ); while( db->get< witness_object, by_name >( db->get_scheduled_witness( 1 ) ).schedule == witness_object::timeshare ) { @@ -3100,10 +3226,139 @@ BOOST_AUTO_TEST_CASE( account_subsidy_witness_limits ) BOOST_CHECK_EQUAL( db->_pending_tx.size(), n+1 ); generate_block(); BOOST_CHECK_EQUAL( db->fetch_block_by_number( db->head_block_num() )->transactions.size(), n ); - BOOST_CHECK_EQUAL( db->_pending_tx.size(), 1 ); + BOOST_CHECK_EQUAL( db->_pending_tx.size(), 1u ); } FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( recurrent_transfer_expiration ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: recurrent transfer expiration" ); + + ACTORS( (alice)(bob) ) + generate_block(); + + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + + fund( "alice", ASSET("100.000 TESTS") ); + fund( "alice", ASSET("100.000 TBD") ); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "100.000 TESTS" ).amount.value ); + + recurrent_transfer_operation op; + op.from = "alice"; + op.to = "bob"; + op.memo = "test"; + op.amount = ASSET( "5.000 TESTS" ); + op.recurrence = 24; + op.executions = 2; + push_transaction(op, alice_private_key); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "100.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "0.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 1 ); + validate_database(); + + generate_block(); + BOOST_TEST_MESSAGE( "--- test initial recurrent transfer execution" ); + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "95.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + validate_database(); + + auto blocks_until_expiration = (fc::hours(op.recurrence * op.executions) + fc::seconds(30)).to_seconds() / HIVE_BLOCK_INTERVAL; + ilog("generating ${blocks} blocks", ("blocks", blocks_until_expiration)); + generate_blocks( blocks_until_expiration ); + + const auto* recurrent_transfer = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) ); + BOOST_TEST_MESSAGE( "--- test recurrent transfer fully executed" ); + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "90.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "10.000 TESTS" ).amount.value ); + BOOST_REQUIRE( recurrent_transfer == nullptr ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + + BOOST_TEST_MESSAGE( "Waiting another 24 hours to see if the recurrent transfer does not execute again past its expiration" ); + + auto blocks_until_next_execution = fc::days(1).to_seconds() / HIVE_BLOCK_INTERVAL; + ilog("generating ${blocks} blocks", ("blocks", blocks_until_next_execution)); + generate_blocks( blocks_until_next_execution ); + + const auto* recurrent_transfer_post_expiration = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) ); + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "90.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "10.000 TESTS" ).amount.value ); + BOOST_REQUIRE( recurrent_transfer_post_expiration == nullptr ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( recurrent_transfer_consecutive_failure_deletion ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: recurrent transfer failure deletion" ); + + ACTORS( (alice)(bob) ) + generate_block(); + + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + + fund( "alice", ASSET("5.000 TESTS") ); + fund( "alice", ASSET("100.000 TBD") ); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + + recurrent_transfer_operation op; + op.from = "alice"; + op.to = "bob"; + op.memo = "test"; + op.amount = ASSET( "5.000 TESTS" ); + op.recurrence = 24; + op.executions = 100; + push_transaction(op, alice_private_key); + + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "0.000 TESTS" ).amount.value ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 1 ); + validate_database(); + + generate_block(); + BOOST_TEST_MESSAGE( "--- test initial recurrent transfer execution" ); + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "0.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + validate_database(); + + auto blocks_until_failure = (fc::days(10) + fc::seconds(60) ).to_seconds() / HIVE_BLOCK_INTERVAL; + ilog("generating ${blocks} blocks", ("blocks", blocks_until_failure)); + generate_blocks( blocks_until_failure ); + + const auto* recurrent_transfer = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) ); + BOOST_TEST_MESSAGE( "--- test recurrent transfer got deleted" ); + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "0.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( recurrent_transfer == nullptr ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + + BOOST_TEST_MESSAGE( "Waiting another 24 hours to see if the recurrent transfer does not execute again past its deletion" ); + + fund( "alice", ASSET("5.000 TESTS") ); + auto blocks_until_next_execution = fc::days(1).to_seconds() / HIVE_BLOCK_INTERVAL; + ilog("generating ${blocks} blocks", ("blocks", blocks_until_next_execution)); + generate_blocks( blocks_until_next_execution ); + + const auto* recurrent_transfer_post_deletion = db->find< recurrent_transfer_object, by_from_to_id >(boost::make_tuple( alice_id, bob_id) ); + BOOST_REQUIRE( get_balance( "alice" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( get_balance( "bob" ).amount.value == ASSET( "5.000 TESTS" ).amount.value ); + BOOST_REQUIRE( recurrent_transfer_post_deletion == nullptr ); + BOOST_REQUIRE( db->get_account( "alice" ).open_recurrent_transfers == 0 ); + validate_database(); + } + FC_LOG_AND_RETHROW() +} + + BOOST_AUTO_TEST_SUITE_END() #endif diff --git a/tests/unit/tests/operation_vistitor.cpp b/tests/unit/tests/operation_vistitor.cpp index 20b23d6f16..a8775e4cd5 100644 --- a/tests/unit/tests/operation_vistitor.cpp +++ b/tests/unit/tests/operation_vistitor.cpp @@ -38,4 +38,33 @@ BOOST_AUTO_TEST_CASE( visit_performance ) { FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE(bad_object) { + bool thrown = false; + bool verified = false; + try { + BOOST_TEST_MESSAGE("Start of static_variant type checks"); + + hive::protocol::account_create_operation testOp; + hive::protocol::operation operation_under_test(testOp); + /// Ok + operation_under_test.get(); + verified = true; + + /// Intentially bad + operation_under_test.get(); + + } + catch(const fc::assert_exception& ae) + { + thrown = true; + BOOST_TEST_MESSAGE("Caught assert exception: " + ae.to_string()); + } + FC_LOG_AND_RETHROW() + + BOOST_CHECK(thrown); + BOOST_CHECK(verified); + + BOOST_TEST_MESSAGE("End of static_variant type checks"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/unit/tests/proposal_tests.cpp b/tests/unit/tests/proposal_tests.cpp index 78038d6d4d..ca03fb297f 100644 --- a/tests/unit/tests/proposal_tests.cpp +++ b/tests/unit/tests/proposal_tests.cpp @@ -74,6 +74,19 @@ int64_t calc_votes( const PROPOSAL_VOTE_IDX& proposal_vote_idx, const std::vecto return cnt; } +struct expired_account_notification_operation_visitor +{ + typedef account_name_type result_type; + mutable std::string debug_type_name; //mangled visited type name - to make it easier to debug + + template + result_type operator()( const T& op ) const { debug_type_name = typeid( T ).name(); return account_name_type(); } + result_type operator()( const expired_account_notification_operation& op ) const + { + debug_type_name = typeid( expired_account_notification_operation ).name(); return op.account; + } +}; + BOOST_FIXTURE_TEST_SUITE( proposal_tests, sps_proposal_database_fixture ) BOOST_AUTO_TEST_CASE( inactive_proposals_have_votes ) @@ -98,7 +111,7 @@ BOOST_AUTO_TEST_CASE( inactive_proposals_have_votes ) auto start_date = db->head_block_time(); auto daily_pay = ASSET( "48.000 TBD" ); - auto hourly_pay = ASSET( "1.996 TBD" );// hourly_pay != ASSET( "2.000 TBD" ) because lack of rounding + auto hourly_pay = ASSET( "2.000 TBD" ); FUND( creator, ASSET( "160.000 TESTS" ) ); FUND( creator, ASSET( "80.000 TBD" ) ); @@ -122,13 +135,13 @@ BOOST_AUTO_TEST_CASE( inactive_proposals_have_votes ) auto end_date_02 = start_date_02 + fc::hours( 48 ); //=====================preparing===================== - const auto& proposal_idx = db->get_index< proposal_index >().indices().get< by_proposal_id >(); + const auto& proposal_idx = db->get_index< proposal_index, by_proposal_id >(); //Needed basic operations int64_t id_proposal_00 = create_proposal( creator, receiver, start_date, end_date, daily_pay, alice_private_key ); - generate_blocks( 1 ); + generate_block(); int64_t id_proposal_01 = create_proposal( creator, receiver, start_date_02, end_date_02, daily_pay, alice_private_key ); - generate_blocks( 1 ); + generate_block(); /* proposal_00 @@ -140,35 +153,35 @@ BOOST_AUTO_TEST_CASE( inactive_proposals_have_votes ) { found_votes_00 = calc_total_votes( proposal_idx, id_proposal_00 ); found_votes_01 = calc_total_votes( proposal_idx, id_proposal_01 ); - BOOST_REQUIRE_EQUAL( found_votes_00, 0 ); - BOOST_REQUIRE_EQUAL( found_votes_01, 0 ); + BOOST_REQUIRE_EQUAL( found_votes_00, 0u ); + BOOST_REQUIRE_EQUAL( found_votes_01, 0u ); } vote_proposal( voter_00, { id_proposal_00 }, true/*approve*/, carol_private_key ); - generate_blocks( 1 ); + generate_block(); { found_votes_00 = calc_total_votes( proposal_idx, id_proposal_00 ); found_votes_01 = calc_total_votes( proposal_idx, id_proposal_01 ); - BOOST_REQUIRE_EQUAL( found_votes_00, 0 ); - BOOST_REQUIRE_EQUAL( found_votes_01, 0 ); + BOOST_REQUIRE_EQUAL( found_votes_00, 0u ); //votes are only counted during maintenance periods (so we don't have to remove them when proxy is set) + BOOST_REQUIRE_EQUAL( found_votes_01, 0u ); } vote_proposal( voter_01, { id_proposal_01 }, true/*approve*/, dan_private_key ); - generate_blocks( 1 ); + generate_block(); { found_votes_00 = calc_total_votes( proposal_idx, id_proposal_00 ); found_votes_01 = calc_total_votes( proposal_idx, id_proposal_01 ); - BOOST_REQUIRE_EQUAL( found_votes_00, 0 ); - BOOST_REQUIRE_EQUAL( found_votes_01, 0 ); + BOOST_REQUIRE_EQUAL( found_votes_00, 0u ); + BOOST_REQUIRE_EQUAL( found_votes_01, 0u ); //like above } //skipping interest generating is necessary transfer( HIVE_INIT_MINER_NAME, receiver, ASSET( "0.001 TBD" )); - generate_block( 5 ); + generate_block(); transfer( HIVE_INIT_MINER_NAME, db->get_treasury_name(), ASSET( "0.001 TBD" ) ); - generate_block( 5 ); + generate_block(); const auto& dgpo = db->get_dynamic_global_properties(); auto old_hbd_supply = dgpo.current_hbd_supply; @@ -189,7 +202,7 @@ BOOST_AUTO_TEST_CASE( inactive_proposals_have_votes ) auto next_block = get_nr_blocks_until_maintenance_block(); generate_blocks( next_block - 1 ); - generate_blocks( 1 ); + generate_block(); auto treasury_hbd_inflation = dgpo.current_hbd_supply - old_hbd_supply; auto after_creator_hbd_balance = _creator.hbd_balance; @@ -215,8 +228,8 @@ BOOST_AUTO_TEST_CASE( inactive_proposals_have_votes ) //Passed ~1h - one reward for `proposal_00` was paid out found_votes_00 = calc_total_votes( proposal_idx, id_proposal_00 ); found_votes_01 = calc_total_votes( proposal_idx, id_proposal_01 ); - BOOST_REQUIRE_GT( found_votes_00, 0 ); - BOOST_REQUIRE_GT( found_votes_01, 0 ); + BOOST_REQUIRE_GT( found_votes_00, 0u ); + BOOST_REQUIRE_GT( found_votes_01, 0u ); } { auto time_movement = start_date + fc::hours( 24 ); @@ -264,6 +277,683 @@ BOOST_AUTO_TEST_CASE( inactive_proposals_have_votes ) FC_LOG_AND_RETHROW() } +BOOST_AUTO_TEST_CASE( db_remove_expired_governance_votes ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: db_remove_expired_governance_votes" ); + ACTORS( (acc1)(acc2)(acc3)(acc4)(acc5)(acc6)(acc7)(acc8)(accw)(accw2)(accp)(pxy) ) + fund( "acc1", 1000 ); vest( "acc1", 1000 ); + fund( "acc2", 2000 ); vest( "acc2", 2000 ); + fund( "acc3", 3000 ); vest( "acc3", 3000 ); + fund( "acc4", 4000 ); vest( "acc4", 4000 ); + fund( "acc5", 5000 ); vest( "acc5", 5000 ); + fund( "acc6", 6000 ); vest( "acc6", 6000 ); + fund( "acc7", 7000 ); vest( "acc7", 7000 ); + fund( "acc8", 8000 ); vest( "acc8", 8000 ); + + generate_block(); + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + const fc::time_point_sec LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS = HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP + HIVE_HARDFORK_1_25_MAX_OLD_GOVERNANCE_VOTE_EXPIRE_SHIFT; + + auto proposal_creator = "accp"; + FUND( proposal_creator, ASSET( "10000.000 TBD" ) ); + + auto start_1 = db->head_block_time(); + auto start_2 = db->head_block_time() + fc::seconds(15); + auto start_3 = db->head_block_time() + fc::seconds(30); + + auto end_1 = LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS + fc::days((100)); + auto end_2 = LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS + fc::days((101)); + auto end_3 = LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS + fc::days((102)); + + int64_t proposal_1 = create_proposal( proposal_creator, "acc1", start_1, end_1, asset( 100, HBD_SYMBOL ), accp_private_key ); + int64_t proposal_2 = create_proposal( proposal_creator, "acc2", start_2, end_2, asset( 100, HBD_SYMBOL ), accp_private_key ); + int64_t proposal_3 = create_proposal( proposal_creator, "acc3", start_3, end_3, asset( 100, HBD_SYMBOL ), accp_private_key ); + + private_key_type accw_witness_key = generate_private_key( "accw_key" ); + witness_create( "accw", accw_private_key, "foo.bar", accw_witness_key.get_public_key(), 1000 ); + private_key_type accw_witness2_key = generate_private_key( "accw2_key" ); + witness_create( "accw2", accw2_private_key, "foo.bar", accw_witness2_key.get_public_key(), 1000 ); + + //if we vote before hardfork 25 + generate_block(); + fc::time_point_sec hardfork_25_time(HIVE_HARDFORK_1_25_TIME); + db_plugin->debug_update( [=]( database& db ) + { + db.modify( db.get_dynamic_global_properties(), [=]( dynamic_global_property_object& gpo ) + { + //fake timestamp of current block so we don't need to wait for creation of 39mln blocks in next line + //even though it is skipping it still takes a lot of time, especially under debugger + gpo.time = hardfork_25_time - fc::days( 202 ); + } ); + } ); + generate_blocks(hardfork_25_time - fc::days(201)); + BOOST_REQUIRE(db->head_block_time() < hardfork_25_time - fc::days(200)); + witness_vote("acc1", "accw2", acc1_private_key); //201 days before HF25 + generate_days_blocks(25); + vote_proposal("acc2", {proposal_1}, true, acc2_private_key); //176 days before HF25 + generate_days_blocks(25); + vote_proposal("acc2", {proposal_2}, true, acc2_private_key); //151 days before HF25 + generate_days_blocks(25); + witness_vote("acc3", "accw", acc3_private_key); //126 days before HF25 + generate_days_blocks(25); + vote_proposal("acc4", {proposal_2}, true, acc4_private_key); //101 days before HF25 + generate_days_blocks(25); + vote_proposal("acc5", {proposal_3}, true, acc5_private_key); //76 days before HF25 + generate_days_blocks(25); + proxy("acc6", "pxy", acc6_private_key); //51 days before HF25 + { + time_point_sec acc_6_vote_expiration_ts_before_proxy_action = db->get_account( "acc6" ).get_governance_vote_expiration_ts(); + //being set as someone's proxy does not affect expiration + BOOST_REQUIRE( db->get_account( "pxy" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum() ); + generate_days_blocks(25); + witness_vote("pxy", "accw2", pxy_private_key); //26 days before HF25 + time_point_sec acc_6_vote_expiration_ts_after_proxy_action = db->get_account( "acc6" ).get_governance_vote_expiration_ts(); + //governance action on a proxy does not affect expiration on account that uses that proxy... + BOOST_REQUIRE( acc_6_vote_expiration_ts_before_proxy_action == acc_6_vote_expiration_ts_after_proxy_action ); + proxy("acc6", "", acc6_private_key); //26 days before HF25 + time_point_sec acc_6_vote_expiration_ts_after_proxy_removal = db->get_account( "acc6" ).get_governance_vote_expiration_ts(); + //...but clearing proxy does + BOOST_REQUIRE( acc_6_vote_expiration_ts_after_proxy_removal == db->get_account( "pxy" ).get_governance_vote_expiration_ts() ); + } + //unvoting proposal (even the one that the account did not vote for before) also resets expiration (same with witness, but you can't unvote witness you didn't vote for) + vote_proposal("acc7", {proposal_2}, false, acc7_private_key); //26 days before HF25 + generate_block(); + + { + time_point_sec acc_1_vote_expiration_ts = db->get_account( "acc1" ).get_governance_vote_expiration_ts(); + time_point_sec acc_2_vote_expiration_ts = db->get_account( "acc2" ).get_governance_vote_expiration_ts(); + time_point_sec acc_3_vote_expiration_ts = db->get_account( "acc3" ).get_governance_vote_expiration_ts(); + time_point_sec acc_4_vote_expiration_ts = db->get_account( "acc4" ).get_governance_vote_expiration_ts(); + time_point_sec acc_5_vote_expiration_ts = db->get_account( "acc5" ).get_governance_vote_expiration_ts(); + time_point_sec acc_6_vote_expiration_ts = db->get_account( "acc6" ).get_governance_vote_expiration_ts(); + time_point_sec acc_7_vote_expiration_ts = db->get_account( "acc7" ).get_governance_vote_expiration_ts(); + time_point_sec pxy_vote_expiration_ts = db->get_account( "pxy" ).get_governance_vote_expiration_ts(); + + BOOST_REQUIRE(acc_1_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && acc_1_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + BOOST_REQUIRE(acc_2_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && acc_2_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + BOOST_REQUIRE(acc_3_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && acc_3_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + BOOST_REQUIRE(acc_4_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && acc_4_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + BOOST_REQUIRE(acc_5_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && acc_5_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + BOOST_REQUIRE(acc_6_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && acc_6_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + BOOST_REQUIRE(acc_7_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && acc_7_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + BOOST_REQUIRE(pxy_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && pxy_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS ); + + const auto& witness_votes = db->get_index(); + BOOST_REQUIRE(witness_votes.size() == 3); + BOOST_REQUIRE(witness_votes.count("acc1") == 1); + BOOST_REQUIRE(witness_votes.count("acc3") == 1); + BOOST_REQUIRE(witness_votes.count("pxy") == 1); + + const auto& proposal_votes = db->get_index(); + BOOST_REQUIRE(proposal_votes.size() == 4); + BOOST_REQUIRE(proposal_votes.count("acc2") == 2); + BOOST_REQUIRE(proposal_votes.count("acc4") == 1); + BOOST_REQUIRE(proposal_votes.count("acc5") == 1); + } + + //make all votes expired. Now vote expiration time is vote time + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD + generate_blocks(LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + + { + BOOST_REQUIRE(db->get_account( "acc1" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc2" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc3" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc4" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc5" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc6" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc7" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + + const auto& witness_votes = db->get_index(); + BOOST_REQUIRE(witness_votes.empty()); + + const auto& proposal_votes = db->get_index(); + BOOST_REQUIRE(proposal_votes.empty()); + } + + witness_vote("acc1", "accw", acc1_private_key); + witness_vote("acc1", "accw2", acc1_private_key); + witness_vote("acc2", "accw", acc2_private_key); + time_point_sec expected_expiration_time = db->head_block_time() + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD; + + //this proposal vote should be removed + vote_proposal("acc3", {proposal_1}, true, acc3_private_key); + generate_block(); + vote_proposal("acc3", {proposal_1}, false, acc3_private_key); + time_point_sec expected_expiration_time_2 = db->head_block_time() + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD; + + //this witness vote should not be removed + generate_days_blocks(1); + witness_vote("acc8", "accw", acc8_private_key); + generate_blocks( expected_expiration_time - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + { + BOOST_REQUIRE(db->get_account( "acc1" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc2" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc3" ).get_governance_vote_expiration_ts() == expected_expiration_time_2); + BOOST_REQUIRE(db->get_account( "acc4" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc5" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc6" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc7" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == expected_expiration_time_2 + fc::days(1)); + + const auto& witness_votes = db->get_index(); + BOOST_REQUIRE(witness_votes.count("acc1") == 2); + BOOST_REQUIRE(witness_votes.count("acc2") == 1); + BOOST_REQUIRE(witness_votes.count("acc8") == 1); + BOOST_REQUIRE(witness_votes.size() == 4); + + const auto& proposal_votes = db->get_index(); + BOOST_REQUIRE(proposal_votes.empty()); + } + generate_block(); + { + BOOST_REQUIRE(db->get_account( "acc1" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc2" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc3" ).get_governance_vote_expiration_ts() == expected_expiration_time_2); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == expected_expiration_time_2 + fc::days(1)); + } + generate_block(); + { + BOOST_REQUIRE(db->get_account( "acc3" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == expected_expiration_time_2 + fc::days(1)); + + const auto& witness_votes = db->get_index(); + BOOST_REQUIRE(witness_votes.count("acc8") == 1); + BOOST_REQUIRE(witness_votes.size() == 1 ); + } + generate_days_blocks(1, false); + { + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + const auto& witness_votes = db->get_index(); + BOOST_REQUIRE(witness_votes.empty()); + } + + vote_proposal("acc3", {proposal_1}, true, acc3_private_key); + vote_proposal("acc3", {proposal_2}, true, acc3_private_key); + vote_proposal("acc4", {proposal_1}, true, acc4_private_key); + expected_expiration_time = db->head_block_time() + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD; + + //in this case we should have 0 witness votes but expiration period should be updated. + witness_vote("acc2", "accw", acc2_private_key); + witness_vote("acc2", "accw2", acc2_private_key); + witness_vote("acc6", "accw", acc6_private_key); + generate_block(); + witness_vote("acc2", "accw", acc2_private_key, false); + witness_vote("acc2", "accw2", acc2_private_key, false); + witness_vote("acc6", "accw", acc6_private_key, false); + expected_expiration_time_2 = db->head_block_time() + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD; + + //this proposal vote should not be removed + generate_days_blocks(1); + vote_proposal("acc8", {proposal_3}, true, acc8_private_key); + + generate_blocks( expected_expiration_time - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + { + BOOST_REQUIRE(db->get_account( "acc1" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc2" ).get_governance_vote_expiration_ts() == expected_expiration_time_2); + BOOST_REQUIRE(db->get_account( "acc3" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc4" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc5" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc6" ).get_governance_vote_expiration_ts() == expected_expiration_time_2); + BOOST_REQUIRE(db->get_account( "acc7" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == expected_expiration_time_2 + fc::days(1)); + + const auto& witness_votes = db->get_index(); + BOOST_REQUIRE(witness_votes.empty()); + + const auto& proposal_votes = db->get_index(); + BOOST_REQUIRE(proposal_votes.count("acc3") == 2); + BOOST_REQUIRE(proposal_votes.count("acc4") == 1); + BOOST_REQUIRE(proposal_votes.count("acc8") == 1); + BOOST_REQUIRE(proposal_votes.size() == 4); + } + generate_block(); + { + BOOST_REQUIRE(db->get_account( "acc2" ).get_governance_vote_expiration_ts() == expected_expiration_time_2); + BOOST_REQUIRE(db->get_account( "acc3" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc4" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc6" ).get_governance_vote_expiration_ts() == expected_expiration_time_2); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == expected_expiration_time_2 + fc::days(1)); + } + generate_block(); + { + BOOST_REQUIRE(db->get_account( "acc2" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc6" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == expected_expiration_time_2 + fc::days(1)); + + const auto& proposal_votes = db->get_index(); + BOOST_REQUIRE(proposal_votes.count("acc8") == 1); + BOOST_REQUIRE(proposal_votes.size() == 1); + } + generate_days_blocks(1, false); + { + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + const auto& proposal_votes = db->get_index(); + BOOST_REQUIRE(proposal_votes.empty()); + } + + witness_vote("acc1", "accw2", acc1_private_key); + vote_proposal("acc4", {proposal_1}, true, acc4_private_key); + witness_vote("acc5", "accw", acc5_private_key); + witness_vote("acc5", "accw2", acc5_private_key); + vote_proposal("acc6", {proposal_1}, true, acc6_private_key); + vote_proposal("acc6", {proposal_2}, true, acc6_private_key); + vote_proposal("acc6", {proposal_3}, true, acc6_private_key); + vote_proposal("acc7", {proposal_1}, true, acc7_private_key); + witness_vote("acc7", "accw", acc7_private_key); + witness_vote("acc8", "accw", acc8_private_key); + expected_expiration_time = db->head_block_time() + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD; + generate_blocks( expected_expiration_time - fc::seconds( HIVE_BLOCK_INTERVAL ) ); + + { + BOOST_REQUIRE(db->get_account( "acc1" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc2" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc3" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc4" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc5" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc6" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc7" ).get_governance_vote_expiration_ts() == expected_expiration_time); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == expected_expiration_time); + + const auto& witness_votes = db->get_index(); + BOOST_REQUIRE(witness_votes.count("acc1") == 1); + BOOST_REQUIRE(witness_votes.count("acc2") == 0); + BOOST_REQUIRE(witness_votes.count("acc3") == 0); + BOOST_REQUIRE(witness_votes.count("acc4") == 0); + BOOST_REQUIRE(witness_votes.count("acc5") == 2); + BOOST_REQUIRE(witness_votes.count("acc6") == 0); + BOOST_REQUIRE(witness_votes.count("acc7") == 1); + BOOST_REQUIRE(witness_votes.count("acc8") == 1); + BOOST_REQUIRE(witness_votes.size() == 5); + + const auto& proposal_votes = db->get_index(); + BOOST_REQUIRE(proposal_votes.count("acc1") == 0); + BOOST_REQUIRE(proposal_votes.count("acc2") == 0); + BOOST_REQUIRE(proposal_votes.count("acc3") == 0); + BOOST_REQUIRE(proposal_votes.count("acc4") == 1); + BOOST_REQUIRE(proposal_votes.count("acc5") == 0); + BOOST_REQUIRE(proposal_votes.count("acc6") == 3); + BOOST_REQUIRE(proposal_votes.count("acc7") == 1); + BOOST_REQUIRE(proposal_votes.count("acc8") == 0); + BOOST_REQUIRE(proposal_votes.size() == 5); + } + generate_block(); + { + BOOST_REQUIRE(db->get_account( "acc1" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc2" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc3" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc4" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc5" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc6" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc7" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + BOOST_REQUIRE(db->get_account( "acc8" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum()); + + const auto& witness_votes = db->get_index(); + BOOST_REQUIRE(witness_votes.empty()); + + const auto& proposal_votes = db->get_index(); + BOOST_REQUIRE(proposal_votes.empty()); + + // first one is producer_reward_operation, later we should have expired_account_notification_operation + const auto& last_operations = get_last_operations(6); + BOOST_REQUIRE(last_operations[0].get().account == "acc8"); + BOOST_REQUIRE(last_operations[1].get().account == "acc7"); + BOOST_REQUIRE(last_operations[2].get().account == "acc6"); + BOOST_REQUIRE(last_operations[3].get().account == "acc5"); + BOOST_REQUIRE(last_operations[4].get().account == "acc4"); + BOOST_REQUIRE(last_operations[5].get().account == "acc1"); + + BOOST_REQUIRE(db->get_account( "acc1" ).witnesses_voted_for == 0); + BOOST_REQUIRE(db->get_account( "acc2" ).witnesses_voted_for == 0); + BOOST_REQUIRE(db->get_account( "acc3" ).witnesses_voted_for == 0); + BOOST_REQUIRE(db->get_account( "acc4" ).witnesses_voted_for == 0); + BOOST_REQUIRE(db->get_account( "acc5" ).witnesses_voted_for == 0); + BOOST_REQUIRE(db->get_account( "acc6" ).witnesses_voted_for == 0); + BOOST_REQUIRE(db->get_account( "acc7" ).witnesses_voted_for == 0); + BOOST_REQUIRE(db->get_account( "acc8" ).witnesses_voted_for == 0); + + time_point_sec first_expiring_ts = db->get_index().begin()->get_governance_vote_expiration_ts(); + BOOST_REQUIRE(first_expiring_ts == fc::time_point_sec::maximum()); + } + + generate_block(); + + //Check if proxy removing works well. acc1 is proxy, acc2 is grandparent proxy + proxy("acc3", "acc1", acc3_private_key); + proxy("acc4", "acc1", acc4_private_key); + generate_blocks(db->head_block_time() + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD); + proxy("acc5", "acc1", acc5_private_key); + proxy("acc1", "acc2", acc1_private_key); + + generate_blocks(2); + BOOST_REQUIRE(!db->get_account("acc3").has_proxy()); + BOOST_REQUIRE(!db->get_account("acc4").has_proxy()); + BOOST_REQUIRE(db->get_account("acc5").get_proxy() == db->get_account("acc1").get_id()); + BOOST_REQUIRE(db->get_account("acc1").get_proxy() == db->get_account("acc2").get_id()); + BOOST_REQUIRE(db->get_account("acc1").proxied_vsf_votes_total() == db->get_account("acc5").get_real_vesting_shares()); + BOOST_REQUIRE(db->get_account("acc2").proxied_vsf_votes_total() == (db->get_account("acc1").get_real_vesting_shares() + db->get_account("acc5").get_real_vesting_shares())); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} +/* +BOOST_AUTO_TEST_CASE( proposals_with_decline_voting_rights ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: proposals_with_decline_voting_rights" ); + ACTORS((acc1)(acc2)(accp)(dwr)) + fund( "dwr", 1000 ); vest( "dwr", 1000 ); + + generate_block(); + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + const fc::time_point_sec LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS = HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP + HIVE_HARDFORK_1_25_MAX_OLD_GOVERNANCE_VOTE_EXPIRE_SHIFT; + const fc::time_point_sec hardfork_25_time( HIVE_HARDFORK_1_25_TIME ); + + FUND( "accp", ASSET( "10000.000 TBD" ) ); + int64_t proposal_1 = create_proposal( "accp", "acc1", hardfork_25_time - fc::days( 50 ), LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS + fc::days( 50 ), asset( 100, HBD_SYMBOL ), accp_private_key ); + int64_t proposal_2 = create_proposal( "accp", "acc2", hardfork_25_time - fc::days( 150 ), LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS + fc::days( 150 ), asset( 100, HBD_SYMBOL ), accp_private_key ); + generate_block(); + + db_plugin->debug_update( [=]( database& db ) + { + db.modify( db.get_dynamic_global_properties(), [=]( dynamic_global_property_object& gpo ) + { + //fake timestamp of current block so we don't need to wait for creation of 39mln blocks in next line + //even though it is skipping it still takes a lot of time, especially under debugger + gpo.time = hardfork_25_time - fc::days( 202 ); + } ); + } ); + generate_blocks( hardfork_25_time - fc::days( 201 ) ); + BOOST_REQUIRE( db->head_block_time() < hardfork_25_time - fc::days( 200 ) ); + + vote_proposal( "dwr", { proposal_1, proposal_2 }, true, dwr_private_key ); + generate_blocks( hardfork_25_time - fc::days( 100 ) ); + //TODO: check balance of acc1 (0) and acc2 (50 days worth of proposal pay) + + { + signed_transaction tx; + decline_voting_rights_operation op; + op.account = "dwr"; + tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); + tx.operations.push_back( op ); + sign( tx, dwr_private_key ); + db->push_transaction( tx, 0 ); + generate_block(); + time_point_sec dwr_vote_expiration_ts = db->get_account( "dwr" ).get_governance_vote_expiration_ts(); + //it takes only 60 seconds in testnet to finish declining, but it is not finished yet + BOOST_REQUIRE( dwr_vote_expiration_ts > HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP && dwr_vote_expiration_ts <= LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS ); + } + generate_blocks( hardfork_25_time - fc::days( 25 ) ); + //TODO: check balance of acc1 (0) and acc2 (50 days worth of proposal pay, maybe more if it caught one more payout before decline finalized) + + //at this point dwr successfully declined voting rights (long ago) - his expiration should be set in stone even if he tries to clear his (nonexisting) votes + BOOST_REQUIRE( db->get_account( "dwr" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum() ); + vote_proposal( "dwr", { proposal_2 }, false, dwr_private_key ); + BOOST_REQUIRE( db->get_account( "dwr" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum() ); + //TODO: decide if finalization of decline should remove existing proposal votes (and if so add tests on number of active votes) + + generate_blocks( LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS ); + generate_block(); + + generate_days_blocks( 25 ); + //TODO: check balance of acc1 and acc2 (no change since last time) + BOOST_REQUIRE( db->get_account( "dwr" ).get_governance_vote_expiration_ts() == fc::time_point_sec::maximum() ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} +*/ + +BOOST_AUTO_TEST_CASE( db_remove_expired_governance_votes_threshold_exceeded ) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: db_remove_expired_governance_votes when threshold stops processing" ); + + ACTORS( + (a00)(a01)(a02)(a03)(a04)(a05)(a06)(a07)(a08)(a09) + (a10)(a11)(a12)(a13)(a14)(a15)(a16)(a17)(a18)(a19) + (a20)(a21)(a22)(a23)(a24)(a25)(a26)(a27)(a28)(a29) + (a30)(a31)(a32)(a33)(a34)(a35)(a36)(a37)(a38)(a39) + (a40)(a41)(a42)(a43)(a44)(a45)(a46)(a47)(a48)(a49) + (a50)(a51)(a52)(a53)(a54)(a55)(a56)(a57)(a58)(a59) + //witnesses + (w00)(w01)(w02)(w03)(w04)(w05)(w06)(w07)(w08)(w09) + (w10)(w11)(w12)(w13)(w14)(w15)(w16)(w17)(w18)(w19) + (w20)(w21)(w22)(w23)(w24)(w25)(w26)(w27)(w28)(w29) + ) + + struct initial_data + { + std::string account; + fc::ecc::private_key key; + }; + + std::vector< initial_data > users = { + {"a00", a00_private_key }, {"a01", a01_private_key }, {"a02", a02_private_key }, {"a03", a03_private_key }, {"a04", a04_private_key }, {"a05", a05_private_key }, {"a06", a06_private_key }, {"a07", a07_private_key }, {"a08", a08_private_key }, {"a09", a09_private_key }, + {"a10", a10_private_key }, {"a11", a11_private_key }, {"a12", a12_private_key }, {"a13", a13_private_key }, {"a14", a14_private_key }, {"a15", a15_private_key }, {"a16", a16_private_key }, {"a17", a17_private_key }, {"a18", a18_private_key }, {"a19", a19_private_key }, + {"a20", a20_private_key }, {"a21", a21_private_key }, {"a22", a22_private_key }, {"a23", a23_private_key }, {"a24", a24_private_key }, {"a25", a25_private_key }, {"a26", a26_private_key }, {"a27", a27_private_key }, {"a28", a28_private_key }, {"a29", a29_private_key }, + {"a30", a30_private_key }, {"a31", a31_private_key }, {"a32", a32_private_key }, {"a33", a33_private_key }, {"a34", a34_private_key }, {"a35", a35_private_key }, {"a36", a36_private_key }, {"a37", a37_private_key }, {"a38", a38_private_key }, {"a39", a39_private_key }, + {"a40", a40_private_key }, {"a41", a41_private_key }, {"a42", a42_private_key }, {"a43", a43_private_key }, {"a44", a44_private_key }, {"a45", a45_private_key }, {"a46", a46_private_key }, {"a47", a47_private_key }, {"a48", a48_private_key }, {"a49", a49_private_key }, + {"a50", a50_private_key }, {"a51", a51_private_key }, {"a52", a52_private_key }, {"a53", a53_private_key }, {"a54", a54_private_key }, {"a55", a55_private_key }, {"a56", a56_private_key }, {"a57", a57_private_key }, {"a58", a58_private_key }, {"a59", a59_private_key }, + }; + + //HIVE_MAX_ACCOUNT_WITNESS_VOTES is 30 for now so only 30 witnesses. + std::vector< initial_data > witnesses = { + {"w00", w00_private_key }, {"w01", w01_private_key }, {"w02", w02_private_key }, {"w03", w03_private_key }, {"w04", w04_private_key }, {"w05", w05_private_key }, {"w06", w06_private_key }, {"w07", w07_private_key }, {"w08", w08_private_key }, {"w09", w09_private_key }, + {"w10", w10_private_key }, {"w11", w11_private_key }, {"w12", w12_private_key }, {"w13", w13_private_key }, {"w14", w14_private_key }, {"w15", w15_private_key }, {"w16", w16_private_key }, {"w17", w17_private_key }, {"w18", w18_private_key }, {"w19", w19_private_key }, + {"w20", w20_private_key }, {"w21", w21_private_key }, {"w22", w22_private_key }, {"w23", w23_private_key }, {"w24", w24_private_key }, {"w25", w25_private_key }, {"w26", w26_private_key }, {"w27", w27_private_key }, {"w28", w28_private_key }, {"w29", w29_private_key } + }; + + for (const auto& witness : witnesses) + { + private_key_type witness_key = generate_private_key( witness.account + "_key" ); + witness_create( witness.account, witness.key, "foo.bar", witness_key.get_public_key(), 1000 ); + } + generate_block(); + + const auto& proposal_vote_idx = db->get_index< proposal_vote_index, by_id >(); + const auto& witness_vote_idx = db->get_index< witness_vote_index, by_id >(); + const auto& account_idx = db->get_index(); + + const fc::time_point_sec LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS = HARDFORK_1_25_FIRST_GOVERNANCE_VOTE_EXPIRE_TIMESTAMP + HIVE_HARDFORK_1_25_MAX_OLD_GOVERNANCE_VOTE_EXPIRE_SHIFT; + db_plugin->debug_update( [=]( database& db ) + { + db.modify( db.get_dynamic_global_properties(), [=]( dynamic_global_property_object& gpo ) + { + //fake timestamp of current block so we don't need to wait for creation of 40mln blocks in next line + //even though it is skipping it still takes a lot of time, especially under debugger + gpo.time = LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS - fc::days( 1 ); + } ); + } ); + generate_blocks(LAST_POSSIBLE_OLD_VOTE_EXPIRE_TS); + + std::vector proposals; + proposals.reserve( users.size() ); + time_point_sec proposals_start_time = db->head_block_time(); + time_point_sec proposals_end_time = proposals_start_time + fc::days(450); + std::string receiver = db->get_treasury_name(); + + for(const auto& user : users ) + { + FUND( user.account, ASSET( "100000.000 TBD" ) ); + proposals.push_back(create_proposal( user.account, receiver, proposals_start_time, proposals_end_time, asset( 100, HBD_SYMBOL ), user.key)); + } + + generate_block(); + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + fc::time_point_sec expiration_time = db->head_block_time() + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD; + for (const auto& user : users) + { + for (const auto proposal : proposals) + vote_proposal(user.account, {proposal}, true, user.key); + + for (const auto& witness : witnesses) + witness_vote(user.account, witness.account, user.key); + + generate_block(); + } + + size_t expected_votes=0, expected_witness_votes=0, expected_expirations=0; + int i; + auto check_vote_count = [&]() + { + auto found_votes = proposal_vote_idx.size(); + auto found_witness_votes = witness_vote_idx.size(); + size_t found_expirations = std::distance( account_idx.begin(), account_idx.upper_bound( expiration_time ) ); + BOOST_REQUIRE_EQUAL( found_votes, expected_votes ); + BOOST_REQUIRE_EQUAL( found_witness_votes, expected_witness_votes ); + BOOST_REQUIRE_EQUAL( found_expirations, expected_expirations ); + }; + + //check that even though accounts expire and fail to clear before another account expires, it still clears in the end, just can take longer + { + auto threshold = db->get_remove_threshold(); + BOOST_REQUIRE_EQUAL( threshold, 20 ); + + generate_blocks( expiration_time - fc::seconds( HIVE_BLOCK_INTERVAL ) ); //note that during skipping automated actions don't work + + expected_votes = 60 * 60; + expected_witness_votes = 60 * 30; + expected_expirations = 0; + expiration_time = db->head_block_time() - fc::seconds(1); + check_vote_count(); + + auto userI = users.begin(); + i = users.size(); + while( expected_votes > 0 ) + { + generate_block(); + expiration_time = db->head_block_time(); + if( i > 0 ) + { + ++expected_expirations; + --i; + } + expected_votes -= threshold; //not all proposal votes of "expired user" are removed due to limitation (user's proposal votes will be removed completely every 60/20 blocks) + if( (expected_votes%60) == 0 ) + { + --expected_expirations; + expected_witness_votes -= 30; //witness votes are always removed completely because there is fixed max number, but we need to actually reach that point - it also moves to next user + const auto& last_operations = get_last_operations( 1 ); + expired_account_notification_operation_visitor v; + account_name_type acc_name = last_operations.back().visit( v ); + BOOST_REQUIRE( acc_name == userI->account ); + ++userI; + } + + check_vote_count(); + } + } + + generate_block(); + + //revote, but in slightly different configuration + //we will be filling 5 times 10 users expiring in the same time, each has 3 proposal votes, with exception of 30..39 range + //however in the same time, with shift of 10, we will be also applying [multi-level] proxy; in the end we expect the following: + //(note that establishing proxy clears witness votes but not proposal votes) + //users[0] .. user[9] - vote for themselves (and become proxy for others) + //users[10] .. user[19] - proxy to above (and have inactive proposal votes) + //users[20] .. user[29] - proxy to above (and have inactive proposal votes) + //users[30] .. user[39] - proxy to above + //users[40] .. user[49] - vote for themselves + //users[50] .. user[59] - don't vote at all + expiration_time = db->head_block_time() + HIVE_GOVERNANCE_VOTE_EXPIRATION_PERIOD; + { + auto userI = users.begin(); + i = 0; + while( i<50 ) + { + if( i < 30 || ( i >= 40 && i < 50 ) ) + vote_proposal( userI->account, { proposals.front(), proposals[1], proposals.back() }, true, userI->key ); + + for( const auto& witness : witnesses ) + witness_vote( userI->account, witness.account, userI->key ); + + if( i >= 10 && i < 40 ) + proxy( userI->account, users[ i-10 ].account, userI->key ); + + if( (++i % 10) == 0 ) + generate_block(); + ++userI; + } + } + + generate_blocks( expiration_time - fc::seconds(HIVE_BLOCK_INTERVAL) ); + //couple of accounts do governance action almost at the last moment (removing of proposal vote, for some that was even inactive, for some there was no such vote anymore - still counts) + for ( i = 0; i < 6; ++i ) + vote_proposal( users[10*i].account, { proposals.front() }, false, users[10*i].key ); + + //check multiple accounts expiring in the same time + { + expected_votes = 3 * 40 - 4; + expected_witness_votes = 20 * 30; + expected_expirations = 0; + expiration_time = db->head_block_time(); + check_vote_count(); + + generate_block(); //users 1..9 expire, but only 1..6 fully clear (2 votes from 7) + expiration_time = db->head_block_time(); + expected_expirations += 9 - 6; + expected_votes -= 20; + expected_witness_votes -= 6 * 30; + check_vote_count(); + + generate_block(); //users 11..19 expire, but 7..9,11..14 clear (1 vote from 15) + expiration_time = db->head_block_time(); + expected_expirations += 9 - 7; + expected_votes -= 20; + expected_witness_votes -= 3 * 30; //11..14 had proxy so no witness votes + check_vote_count(); + + generate_block(); //users 21..29 expire, but 15..19,21..22 clear (0 votes from 23) + expiration_time = db->head_block_time(); + expected_expirations += 9 - 7; + expected_votes -= 20; + expected_witness_votes -= 0 * 30; //all cleared had proxy, no witness votes + check_vote_count(); + + generate_block(); //users 31..39 expire, but 23..28 clear (2 votes from 29) + expiration_time = db->head_block_time(); + expected_expirations += 9 - 6; + expected_votes -= 20; + expected_witness_votes -= 0 * 30; //all cleared had proxy, no witness votes + check_vote_count(); + + generate_block(); //users 41..49 expire, but 29,31..39,41..46 clear (1 vote from 47) + expiration_time = db->head_block_time(); + expected_expirations += 9 - 16; + expected_votes -= 20; + expected_witness_votes -= 6 * 30; //only 41..46 had witness votes + check_vote_count(); + + generate_block(); //no new users expire, 47..49 clear (unused limit is 12) + expiration_time = db->head_block_time(); + expected_expirations = 0; //6 users are active (long expiration time), rest is fully expired + expected_votes = 8; //0,10,20,40 have 2 votes active each + expected_witness_votes = 2 * 30; //0,30 have 30 witness votes active each + check_vote_count(); + } + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + + BOOST_AUTO_TEST_CASE( generating_payments ) { try @@ -283,7 +973,7 @@ BOOST_AUTO_TEST_CASE( generating_payments ) auto start_date = db->head_block_time(); auto daily_pay = ASSET( "48.000 TBD" ); - auto hourly_pay = ASSET( "1.996 TBD" );// hourly_pay != ASSET( "2.000 TBD" ) because lack of rounding + auto hourly_pay = ASSET( "2.000 TBD" ); FUND( creator, ASSET( "160.000 TESTS" ) ); FUND( creator, ASSET( "80.000 TBD" ) ); @@ -306,7 +996,6 @@ BOOST_AUTO_TEST_CASE( generating_payments ) vote_proposal( voter_01, { id_proposal_00 }, true/*approve*/, carol_private_key ); generate_blocks( 1 ); - //skipping interest generating is necessary transfer( HIVE_INIT_MINER_NAME, receiver, ASSET( "0.001 TBD" )); generate_block( 5 ); @@ -399,7 +1088,7 @@ BOOST_AUTO_TEST_CASE( generating_payments_01 ) auto end_date = start_date + end_time_shift; auto daily_pay = ASSET( "24.000 TBD" ); - auto paid = ASSET( "4.990 TBD" );// paid != ASSET( "5.000 TBD" ) because lack of rounding + auto paid = ASSET( "5.000 TBD" ); FUND( db->get_treasury_name(), ASSET( "5000000.000 TBD" ) ); //=====================preparing===================== @@ -502,7 +1191,6 @@ BOOST_AUTO_TEST_CASE( generating_payments_02 ) for( auto item : inits ) { vote_proposal( item.account, {0}, true/*approve*/, item.key); - generate_block(); const account_object& account = db->get_account( item.account ); @@ -528,6 +1216,14 @@ BOOST_AUTO_TEST_CASE( generating_payments_02 ) BOOST_REQUIRE( found_votes == 10 ); } + { + generate_block(); + auto found_proposals = calc_proposals( proposal_idx, { 0 } ); + auto found_votes = calc_proposal_votes( proposal_vote_idx, 0 ); + BOOST_REQUIRE( found_proposals == 0 ); + BOOST_REQUIRE( found_votes == 0 ); + } + for( auto item : inits ) { const account_object& account = db->get_account( item.account ); @@ -650,20 +1346,11 @@ BOOST_AUTO_TEST_CASE( generating_payments_03 ) `tester02` - no payout, because of lack of votes */ ilog(""); - payment_checker( { ASSET( "0.998 TBD" ), ASSET( "0.998 TBD" ), ASSET( "0.000 TBD" ) } ); - //ideally: ASSET( "1.000 TBD" ), ASSET( "1.000 TBD" ), ASSET( "0.000 TBD" ) but there is lack of rounding + payment_checker( { ASSET("1.000 TBD" ), ASSET( "1.000 TBD" ), ASSET( "0.000 TBD" ) } ); { BOOST_TEST_MESSAGE( "Setting proxy. The account `tester01` don't want to vote. Every decision is made by account `tester00`" ); - account_witness_proxy_operation op; - op.account = tester01_account; - op.proxy = tester00_account; - - signed_transaction tx; - tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); - tx.operations.push_back( op ); - sign( tx, inits[ tester01_account ] ); - db->push_transaction( tx, 0 ); + proxy( tester01_account, tester00_account, inits[ tester01_account ] ); } generate_blocks( start_date + end_time_shift[ interval++ ] + fc::seconds( 10 ), false ); @@ -673,8 +1360,7 @@ BOOST_AUTO_TEST_CASE( generating_payments_03 ) `tester02` - no payout, because of lack of votes */ ilog(""); - payment_checker( { ASSET( "1.996 TBD" ), ASSET( "0.998 TBD" ), ASSET( "0.000 TBD" ) } ); - //ideally: ASSET( "2.000 TBD" ), ASSET( "1.000 TBD" ), ASSET( "0.000 TBD" ) but there is lack of rounding + payment_checker( { ASSET( "2.000 TBD" ), ASSET( "1.000 TBD" ), ASSET( "0.000 TBD" ) } ); vote_proposal( tester02_account, {2}, true/*approve*/, inits[ tester02_account ] ); generate_block(); @@ -686,20 +1372,11 @@ BOOST_AUTO_TEST_CASE( generating_payments_03 ) `tester02` - got payout, because voted for his proposal */ ilog(""); - payment_checker( { ASSET( "2.994 TBD" ), ASSET( "0.998 TBD" ), ASSET( "2082.369 TBD" ) } ); - //ideally: ASSET( "3.000 TBD" ), ASSET( "1.000 TBD" ), ASSET( "2082.346 TBD" ) but there is lack of rounding + payment_checker( { ASSET( "3.000 TBD" ), ASSET( "1.000 TBD" ), ASSET( "2082.367 TBD" ) } ); { BOOST_TEST_MESSAGE( "Proxy doesn't exist. Now proposal with id = 3 has the most votes. This proposal grabs all payouts." ); - account_witness_proxy_operation op; - op.account = tester01_account; - op.proxy = ""; - - signed_transaction tx; - tx.set_expiration( db->head_block_time() + HIVE_MAX_TIME_UNTIL_EXPIRATION ); - tx.operations.push_back( op ); - sign( tx, inits[ tester01_account ] ); - db->push_transaction( tx, 0 ); + proxy( tester01_account, "", inits[ tester01_account ] ); } generate_blocks( start_date + end_time_shift[ interval++ ] + fc::seconds( 10 ), false ); @@ -709,8 +1386,261 @@ BOOST_AUTO_TEST_CASE( generating_payments_03 ) `tester02` - got payout, because voted for his proposal */ ilog(""); - payment_checker( { ASSET( "2.994 TBD" ), ASSET( "0.998 TBD" ), ASSET( "4164.874 TBD" ) } ); - //ideally: ASSET( "3.000 TBD" ), ASSET( "1.000 TBD" ), ASSET( "4164.824 TBD" ) but there is lack of rounding + payment_checker( { ASSET( "3.000 TBD" ), ASSET( "1.000 TBD" ), ASSET( "4164.872 TBD" ) } ); + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( generating_payments_04 ) +{ +try + { + BOOST_TEST_MESSAGE( "Testing: payment truncating" ); + + ACTORS( (alice)(bob)(carol)(dave) ) + generate_block(); + + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + //=====================preparing===================== + auto creator = "alice"; + auto receiver = "bob"; + + auto start_date = db->head_block_time(); + + auto daily_pay = ASSET( "2378.447 TBD" ); + auto hourly_pay = ASSET( "99.101 TBD" ); //99.101958(3) + + FUND( creator, ASSET( "160.000 TESTS" ) ); + FUND( creator, ASSET( "80.000 TBD" ) ); + FUND( db->get_treasury_name(), ASSET( "500000.000 TBD" ) ); + + auto voter_01 = "carol"; + + vest(HIVE_INIT_MINER_NAME, voter_01, ASSET( "1.000 TESTS" )); + + //Due to the `delaying votes` algorithm, generate blocks for 30 days in order to activate whole votes' pool ( take a look at `HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS` ) + start_date += fc::seconds( HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS ); + generate_blocks( start_date ); + + auto end_date = start_date + fc::days( 2 ); + //=====================preparing===================== + + //Needed basic operations + int64_t id_proposal_00 = create_proposal( creator, receiver, start_date, end_date, daily_pay, alice_private_key ); + generate_block(); + + vote_proposal( voter_01, { id_proposal_00 }, true/*approve*/, carol_private_key ); + generate_block(); + + //skipping interest generating is necessary + transfer( HIVE_INIT_MINER_NAME, receiver, ASSET( "0.001 TBD" )); + generate_block(); + transfer( HIVE_INIT_MINER_NAME, db->get_treasury_name(), ASSET( "0.001 TBD" ) ); + generate_block(); + + const auto& dgpo = db->get_dynamic_global_properties(); + auto old_hbd_supply = dgpo.get_current_hbd_supply(); + + + const account_object& _creator = db->get_account( creator ); + const account_object& _receiver = db->get_account( receiver ); + const account_object& _voter_01 = db->get_account( voter_01 ); + const account_object& _treasury = db->get_treasury(); + + { + BOOST_TEST_MESSAGE( "---Payment---" ); + + auto before_creator_hbd_balance = _creator.get_hbd_balance(); + auto before_receiver_hbd_balance = _receiver.get_hbd_balance(); + auto before_voter_01_hbd_balance = _voter_01.get_hbd_balance(); + auto before_treasury_hbd_balance = _treasury.get_hbd_balance(); + + auto next_block = get_nr_blocks_until_maintenance_block(); + generate_blocks( next_block - 1 ); + generate_block(); + + auto treasury_hbd_inflation = dgpo.get_current_hbd_supply() - old_hbd_supply; + auto after_creator_hbd_balance = _creator.get_hbd_balance(); + auto after_receiver_hbd_balance = _receiver.get_hbd_balance(); + auto after_voter_01_hbd_balance = _voter_01.get_hbd_balance(); + auto after_treasury_hbd_balance = _treasury.get_hbd_balance(); + + BOOST_REQUIRE( before_creator_hbd_balance == after_creator_hbd_balance ); + + BOOST_REQUIRE( before_receiver_hbd_balance == after_receiver_hbd_balance - hourly_pay ); + BOOST_REQUIRE( before_voter_01_hbd_balance == after_voter_01_hbd_balance ); + BOOST_REQUIRE( before_treasury_hbd_balance == after_treasury_hbd_balance - treasury_hbd_inflation + hourly_pay ); + } + + validate_database(); + } +FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( generating_payments_05 ) +{ +try + { + BOOST_TEST_MESSAGE( "Testing: payment not truncating" ); + + ACTORS( (alice)(bob)(carol)(dave) ) + generate_block(); + + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + //=====================preparing===================== + auto creator = "alice"; + auto receiver = "bob"; + + auto start_date = db->head_block_time(); + + auto daily_pay = ASSET( "2378.448 TBD" ); + auto hourly_pay = ASSET( "99.102 TBD" ); + + FUND( creator, ASSET( "160.000 TESTS" ) ); + FUND( creator, ASSET( "80.000 TBD" ) ); + FUND( db->get_treasury_name(), ASSET( "500000.000 TBD" ) ); + + auto voter_01 = "carol"; + + vest(HIVE_INIT_MINER_NAME, voter_01, ASSET( "1.000 TESTS" )); + + //Due to the `delaying votes` algorithm, generate blocks for 30 days in order to activate whole votes' pool ( take a look at `HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS` ) + start_date += fc::seconds( HIVE_DELAYED_VOTING_TOTAL_INTERVAL_SECONDS ); + generate_blocks( start_date ); + + auto end_date = start_date + fc::days( 2 ); + //=====================preparing===================== + + //Needed basic operations + int64_t id_proposal_00 = create_proposal( creator, receiver, start_date, end_date, daily_pay, alice_private_key ); + generate_block(); + + vote_proposal( voter_01, { id_proposal_00 }, true/*approve*/, carol_private_key ); + generate_block(); + + //skipping interest generating is necessary + transfer( HIVE_INIT_MINER_NAME, receiver, ASSET( "0.001 TBD" )); + generate_block(); + transfer( HIVE_INIT_MINER_NAME, db->get_treasury_name(), ASSET( "0.001 TBD" ) ); + generate_block(); + + const auto& dgpo = db->get_dynamic_global_properties(); + auto old_hbd_supply = dgpo.get_current_hbd_supply(); + + + const account_object& _creator = db->get_account( creator ); + const account_object& _receiver = db->get_account( receiver ); + const account_object& _voter_01 = db->get_account( voter_01 ); + const account_object& _treasury = db->get_treasury(); + + { + BOOST_TEST_MESSAGE( "---Payment---" ); + + auto before_creator_hbd_balance = _creator.get_hbd_balance(); + auto before_receiver_hbd_balance = _receiver.get_hbd_balance(); + auto before_voter_01_hbd_balance = _voter_01.get_hbd_balance(); + auto before_treasury_hbd_balance = _treasury.get_hbd_balance(); + + auto next_block = get_nr_blocks_until_maintenance_block(); + generate_blocks( next_block - 1 ); + generate_block(); + + auto treasury_hbd_inflation = dgpo.get_current_hbd_supply() - old_hbd_supply; + auto after_creator_hbd_balance = _creator.get_hbd_balance(); + auto after_receiver_hbd_balance = _receiver.get_hbd_balance(); + auto after_voter_01_hbd_balance = _voter_01.get_hbd_balance(); + auto after_treasury_hbd_balance = _treasury.get_hbd_balance(); + + BOOST_REQUIRE( before_creator_hbd_balance == after_creator_hbd_balance ); + BOOST_REQUIRE( before_receiver_hbd_balance == after_receiver_hbd_balance - hourly_pay ); + BOOST_REQUIRE( before_voter_01_hbd_balance == after_voter_01_hbd_balance ); + BOOST_REQUIRE( before_treasury_hbd_balance == after_treasury_hbd_balance - treasury_hbd_inflation + hourly_pay ); + } + + validate_database(); + } + FC_LOG_AND_RETHROW() +} + +BOOST_AUTO_TEST_CASE( expired_proposals_forbidden_voting) +{ + try + { + BOOST_TEST_MESSAGE( "Testing: when proposals are expired, then voting on such proposals are not allowed" ); + + ACTORS( (alice)(bob)(carol)(carol1)(carol2) ) + generate_block(); + + set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); + generate_block(); + + //=====================preparing===================== + auto creator = "alice"; + auto receiver = "bob"; + + auto start_time = db->head_block_time(); + + auto start_date_00 = start_time + fc::seconds( 30 ); + auto end_date_00 = start_time + fc::minutes( 100 ); + + auto start_date_01 = start_time + fc::seconds( 40 ); + auto end_date_01 = start_time + fc::minutes( 300 ); + + auto start_date_02 = start_time + fc::seconds( 50 ); + auto end_date_02 = start_time + fc::minutes( 200 ); + + auto daily_pay = asset( 100, HBD_SYMBOL ); + + FUND( creator, ASSET( "100.000 TBD" ) ); + //=====================preparing===================== + + int64_t id_proposal_00 = create_proposal( creator, receiver, start_date_00, end_date_00, daily_pay, alice_private_key ); + generate_block(); + + int64_t id_proposal_01 = create_proposal( creator, receiver, start_date_01, end_date_01, daily_pay, alice_private_key ); + generate_block(); + + int64_t id_proposal_02 = create_proposal( creator, receiver, start_date_02, end_date_02, daily_pay, alice_private_key ); + generate_block(); + + { + vote_proposal( "carol", { id_proposal_00, id_proposal_01, id_proposal_02 }, true/*approve*/, carol_private_key ); + generate_blocks( 1 ); + vote_proposal( "carol", { id_proposal_00, id_proposal_01, id_proposal_02 }, false/*approve*/, carol_private_key ); + generate_blocks( 1 ); + } + start_time = db->head_block_time(); + { + generate_blocks( start_time + fc::minutes( 110 ) ); + HIVE_REQUIRE_THROW( vote_proposal( "carol", { id_proposal_00 }, true/*approve*/, carol_private_key ), fc::exception); + vote_proposal( "carol", { id_proposal_01 }, true/*approve*/, carol_private_key ); + vote_proposal( "carol", { id_proposal_02 }, true/*approve*/, carol_private_key ); + generate_blocks( 1 ); + } + { + generate_blocks( start_time + fc::minutes( 210 ) ); + HIVE_REQUIRE_THROW( vote_proposal( "carol1", { id_proposal_00 }, true/*approve*/, carol1_private_key ), fc::exception); + vote_proposal( "carol1", { id_proposal_01 }, true/*approve*/, carol1_private_key ); + HIVE_REQUIRE_THROW( vote_proposal( "carol1", { id_proposal_02 }, true/*approve*/, carol1_private_key ), fc::exception); + generate_blocks( 1 ); + } + { + generate_blocks( start_time + fc::minutes( 310 ) ); + HIVE_REQUIRE_THROW( vote_proposal( "carol2", { id_proposal_00 }, true/*approve*/, carol2_private_key ), fc::exception); + HIVE_REQUIRE_THROW( vote_proposal( "carol2", { id_proposal_01 }, true/*approve*/, carol2_private_key ), fc::exception); + HIVE_REQUIRE_THROW( vote_proposal( "carol2", { id_proposal_02 }, true/*approve*/, carol2_private_key ), fc::exception); + generate_blocks( 1 ); + } + + BOOST_REQUIRE( exist_proposal( id_proposal_00 ) ); + BOOST_REQUIRE( exist_proposal( id_proposal_01 ) ); + BOOST_REQUIRE( exist_proposal( id_proposal_02 ) ); validate_database(); } @@ -829,7 +1759,7 @@ BOOST_AUTO_TEST_CASE( proposal_object_apply ) auto subject = "hello"; auto permlink = "somethingpermlink"; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); FUND( creator, ASSET( "80.000 TBD" ) ); @@ -919,7 +1849,7 @@ BOOST_AUTO_TEST_CASE( proposal_object_apply_free_increase ) auto subject = "hello"; auto permlink = "somethingpermlink"; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); FUND( creator, ASSET( "80.000 TBD" ) ); @@ -2211,7 +3141,7 @@ BOOST_AUTO_TEST_CASE( proposals_maintenance_01 ) generate_blocks( start_time + ( start_time_shift + end_time_shift - block_interval ) ); - auto threshold = db->get_sps_remove_threshold(); + auto threshold = db->get_remove_threshold(); auto nr_stages = current_active_proposals / threshold; for( auto i = 0; i < nr_stages; ++i ) @@ -2317,7 +3247,7 @@ BOOST_AUTO_TEST_CASE( proposals_maintenance_02 ) generate_blocks( start_time + ( start_time_shift + end_time_shift - block_interval ) ); - auto threshold = db->get_sps_remove_threshold(); + auto threshold = db->get_remove_threshold(); auto current_active_anything = current_active_proposals + current_active_votes; auto nr_stages = current_active_anything / threshold; @@ -2413,7 +3343,7 @@ BOOST_AUTO_TEST_CASE( proposals_removing_with_threshold ) auto current_active_votes = current_active_proposals * static_cast< int16_t > ( inits.size() ); BOOST_REQUIRE( calc_votes( proposal_vote_idx, proposals_id ) == current_active_votes ); - auto threshold = db->get_sps_remove_threshold(); + auto threshold = db->get_remove_threshold(); BOOST_REQUIRE( threshold == 20 ); flat_set< int64_t > _proposals_id( proposals_id.begin(), proposals_id.end() ); @@ -2518,7 +3448,7 @@ BOOST_AUTO_TEST_CASE( proposals_removing_with_threshold_01 ) auto current_active_votes = current_active_proposals * static_cast< int16_t > ( inits.size() ); BOOST_REQUIRE( calc_votes( proposal_vote_idx, proposals_id ) == current_active_votes ); - auto threshold = db->get_sps_remove_threshold(); + auto threshold = db->get_remove_threshold(); BOOST_REQUIRE( threshold == 20 ); /* @@ -2760,7 +3690,7 @@ BOOST_AUTO_TEST_CASE( proposals_removing_with_threshold_02 ) auto current_active_votes = current_active_proposals * static_cast< int16_t > ( inits.size() ); BOOST_REQUIRE( calc_votes( proposal_vote_idx, proposals_id ) == current_active_votes ); - auto threshold = db->get_sps_remove_threshold(); + auto threshold = db->get_remove_threshold(); BOOST_REQUIRE( threshold == 20 ); /* @@ -3020,8 +3950,10 @@ BOOST_AUTO_TEST_CASE( update_proposal_000 ) auto subject = "hello"; auto permlink = "somethingpermlink"; + auto new_permlink = "somethingpermlink2"; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, new_permlink, "title", "body", "test", alice_private_key); FUND( creator, ASSET( "80.000 TBD" ) ); @@ -3062,10 +3994,32 @@ BOOST_AUTO_TEST_CASE( update_proposal_000 ) BOOST_REQUIRE( found->permlink == permlink ); BOOST_TEST_MESSAGE("-- updating"); - update_proposal(found->proposal_id, creator, daily_pay, "Other subject", permlink, alice_private_key); + + time_point_sec new_end_date = end_date - fc::days( 1 ); + auto new_subject = "Other subject"; + auto new_daily_pay = asset( 100, HBD_SYMBOL ); + + update_proposal(found->proposal_id, creator, new_daily_pay, new_subject, new_permlink, alice_private_key); generate_block(); found = proposal_idx.find( creator ); - BOOST_REQUIRE( found->subject == "Other subject" ); + BOOST_REQUIRE( found->creator == creator ); + BOOST_REQUIRE( found->receiver == receiver ); + BOOST_REQUIRE( found->start_date == start_date ); + BOOST_REQUIRE( found->end_date == end_date ); + BOOST_REQUIRE( found->daily_pay == new_daily_pay ); + BOOST_REQUIRE( found->subject == new_subject ); + BOOST_REQUIRE( found->permlink == new_permlink ); + + update_proposal(found->proposal_id, creator, new_daily_pay, new_subject, new_permlink, alice_private_key, &new_end_date); + generate_block(); + found = proposal_idx.find( creator ); + BOOST_REQUIRE( found->creator == creator ); + BOOST_REQUIRE( found->receiver == receiver ); + BOOST_REQUIRE( found->start_date == start_date ); + BOOST_REQUIRE( found->end_date == new_end_date ); + BOOST_REQUIRE( found->daily_pay == new_daily_pay ); + BOOST_REQUIRE( found->subject == new_subject ); + BOOST_REQUIRE( found->permlink == new_permlink ); validate_database(); } FC_LOG_AND_RETHROW() @@ -3085,7 +4039,7 @@ BOOST_AUTO_TEST_CASE( update_proposal_001 ) auto creator = "alice"; auto receiver = "bob"; - auto start_date = db->head_block_time() + fc::days( 1 ); + time_point_sec start_date = db->head_block_time() + fc::days( 1 ); auto end_date = start_date + fc::days( 2 ); auto daily_pay = asset( 100, HBD_SYMBOL ); auto subject = "hello"; @@ -3095,7 +4049,7 @@ BOOST_AUTO_TEST_CASE( update_proposal_001 ) signed_transaction tx; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); create_proposal_operation op; op.creator = creator; @@ -3143,7 +4097,7 @@ BOOST_AUTO_TEST_CASE( update_proposal_002 ) signed_transaction tx; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); create_proposal_operation op; op.creator = creator; @@ -3193,7 +4147,7 @@ BOOST_AUTO_TEST_CASE( update_proposal_003 ) signed_transaction tx; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); create_proposal_operation op; op.creator = creator; @@ -3246,7 +4200,7 @@ BOOST_AUTO_TEST_CASE( update_proposal_004 ) signed_transaction tx; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); create_proposal_operation op; op.creator = creator; @@ -3266,8 +4220,11 @@ BOOST_AUTO_TEST_CASE( update_proposal_004 ) const auto& proposal_idx = db->get_index< proposal_index >().indices().get< by_creator >(); auto proposal = proposal_idx.find( creator ); - + // Empty subject HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), "", permlink, alice_private_key), fc::exception); + // Subject too long + HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + permlink, alice_private_key), fc::exception); validate_database(); } FC_LOG_AND_RETHROW() @@ -3277,8 +4234,8 @@ BOOST_AUTO_TEST_CASE( update_proposal_005 ) { try { - BOOST_TEST_MESSAGE( "Testing: update proposal: operation arguments validation - invalid subject" ); - ACTORS( (alice)(bob) ) + BOOST_TEST_MESSAGE( "Testing: update proposal: operation arguments validation - invalid permlink" ); + ACTORS( (alice)(bob)(dave) ) generate_block(); set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); @@ -3286,7 +4243,7 @@ BOOST_AUTO_TEST_CASE( update_proposal_005 ) auto creator = "alice"; auto receiver = "bob"; - auto start_date = db->head_block_time() + fc::days( 1 ); + fc::time_point_sec start_date = db->head_block_time() + fc::days( 1 ); auto end_date = start_date + fc::days( 2 ); auto daily_pay = asset( 100, HBD_SYMBOL ); auto subject = "hello"; @@ -3296,7 +4253,8 @@ BOOST_AUTO_TEST_CASE( update_proposal_005 ) signed_transaction tx; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); + post_comment_with_block_generation("dave", "davepermlink", "title", "body", "test", dave_private_key); create_proposal_operation op; op.creator = creator; @@ -3316,22 +4274,24 @@ BOOST_AUTO_TEST_CASE( update_proposal_005 ) const auto& proposal_idx = db->get_index< proposal_index >().indices().get< by_creator >(); auto proposal = proposal_idx.find( creator ); - // Empty subject - HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), "", permlink, alice_private_key), fc::exception); - // Subject too long - HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - permlink, alice_private_key), fc::exception); + // Empty permlink + HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), subject, "", alice_private_key), fc::exception); + // Post doesn't exist + HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), subject, "doesntexist", alice_private_key), fc::exception); + // Post exists but is made by an user that is neither the receiver or the creator + HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), subject, "davepermlink", alice_private_key), fc::exception); validate_database(); } FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_CASE( update_proposal_006 ) { try { - BOOST_TEST_MESSAGE( "Testing: update proposal: operation arguments validation - invalid permlink" ); - ACTORS( (alice)(bob)(dave) ) + BOOST_TEST_MESSAGE( "Testing: update proposal: operation arguments validation - invalid end_date" ); + ACTORS( (alice)(bob) ) generate_block(); set_price_feed( price( ASSET( "1.000 TBD" ), ASSET( "1.000 TESTS" ) ) ); @@ -3339,8 +4299,9 @@ BOOST_AUTO_TEST_CASE( update_proposal_006 ) auto creator = "alice"; auto receiver = "bob"; - auto start_date = db->head_block_time() + fc::days( 1 ); + fc::time_point_sec start_date = db->head_block_time() + fc::days( 1 ); auto end_date = start_date + fc::days( 2 ); + fc::time_point_sec end_date_invalid = start_date + fc::days( 3 ); auto daily_pay = asset( 100, HBD_SYMBOL ); auto subject = "hello"; auto permlink = "somethingpermlink"; @@ -3349,8 +4310,7 @@ BOOST_AUTO_TEST_CASE( update_proposal_006 ) signed_transaction tx; - post_comment(creator, permlink, "title", "body", "test", alice_private_key); - post_comment("dave", "davepermlink", "title", "body", "test", dave_private_key); + post_comment_with_block_generation(creator, permlink, "title", "body", "test", alice_private_key); create_proposal_operation op; op.creator = creator; @@ -3370,12 +4330,8 @@ BOOST_AUTO_TEST_CASE( update_proposal_006 ) const auto& proposal_idx = db->get_index< proposal_index >().indices().get< by_creator >(); auto proposal = proposal_idx.find( creator ); - // Empty permlink - HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), subject, "", alice_private_key), fc::exception); - // Post doesn't exist - HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), subject, "doesntexist", alice_private_key), fc::exception); - // Post exists but is made by an user that is neither the receiver or the creator - HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), subject, "davepermlink", alice_private_key), fc::exception); + HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), subject, permlink, alice_private_key, &start_date), fc::exception); + HIVE_REQUIRE_THROW( update_proposal(proposal->proposal_id, creator, asset( 110, HBD_SYMBOL ), subject, permlink, alice_private_key, &end_date_invalid), fc::exception); validate_database(); } FC_LOG_AND_RETHROW() @@ -3557,7 +4513,7 @@ BOOST_AUTO_TEST_CASE( proposals_removing_with_threshold_03 ) auto current_active_votes = current_active_proposals * static_cast< int16_t > ( inits.size() ); BOOST_REQUIRE( calc_votes( proposal_vote_idx, proposals_id ) == current_active_votes ); - auto threshold = db->get_sps_remove_threshold(); + auto threshold = db->get_remove_threshold(); BOOST_REQUIRE( threshold == -1 ); generate_blocks( start_time + fc::seconds( HIVE_PROPOSAL_MAINTENANCE_CLEANUP ) ); @@ -3589,6 +4545,7 @@ BOOST_AUTO_TEST_CASE( proposals_removing_with_threshold_03 ) FC_LOG_AND_RETHROW() } +// This generating payment is part of proposal_tests_performance BOOST_AUTO_TEST_CASE( generating_payments ) { try @@ -3640,7 +4597,17 @@ BOOST_AUTO_TEST_CASE( generating_payments ) auto end_date = start_date + end_time_shift; auto daily_pay = ASSET( "24.000 TBD" ); - auto paid = ASSET( "1.000 TBD" );//because only 1 hour + auto paid = ASSET( "1.000 TBD" ); // because only 1 hour + + /* This unit test triggers an edge case where the maintenance window for proposals happens after 1h and 3s (one block) + due to block skipping (in mainnet that can happen if maintenance block is missed). + normal case: + 3600 (passed seconds) * 24000 (daily pay) / 86400 (seconds in day) = 1000 => 1.000 TBD + edge case: + 3603 * 24000 / 86400 = 1000.833333 -> 1000 => still 1.000 TBD + It is possible to miss more blocks and as a result get even longer time covered by maintenance giving more than + expected pay in single maintenance, but in the same time payout periods will drift even more. + */ FUND( db->get_treasury_name(), ASSET( "5000000.000 TBD" ) ); //=====================preparing===================== diff --git a/tests/unit/tests/undo_tests.cpp b/tests/unit/tests/undo_tests.cpp index e56f73e238..030f1a8d1e 100644 --- a/tests/unit/tests/undo_tests.cpp +++ b/tests/unit/tests/undo_tests.cpp @@ -37,6 +37,7 @@ BOOST_AUTO_TEST_CASE( undo_basic ) undo_db udb( *db ); undo_scenario< account_object > ao( *db ); + const account_object& pxy = ao.create( "proxy00" ); BOOST_TEST_MESSAGE( "--- No object added" ); ao.remember_old_values< account_index >(); @@ -81,7 +82,7 @@ BOOST_AUTO_TEST_CASE( undo_basic ) udb.undo_begin(); const account_object& obj4 = ao.create( "name00" ); - ao.modify( obj4, [&]( account_object& obj ){ obj.proxy = "proxy00"; } ); + ao.modify( obj4, [&]( account_object& obj ){ obj.set_proxy(pxy); } ); ao.remove( obj4 ); udb.undo_end(); @@ -103,7 +104,7 @@ BOOST_AUTO_TEST_CASE( undo_basic ) udb.undo_begin(); const account_object& obj6 = ao.create( "name00" ); - ao.modify( obj6, [&]( account_object& obj ){ obj.proxy = "proxy00"; } ); + ao.modify( obj6, [&]( account_object& obj ){ obj.set_proxy(pxy); } ); ao.remove( obj6 ); ao.create( "name00" ); @@ -141,12 +142,15 @@ BOOST_AUTO_TEST_CASE( undo_object_disappear ) undo_db udb( *db ); undo_scenario< account_object > ao( *db ); + const account_object& pxy0 = ao.create( "proxy00" ); + const account_object& pxy1 = ao.create( "proxy01" ); + uint32_t old_size = ao.size< account_index >(); - const account_object& obj0 = ao.create( "name00" ); ao.modify( obj0, [&]( account_object& obj ){ obj.proxy = "proxy00"; } ); + const account_object& obj0 = ao.create( "name00" ); ao.modify( obj0, [&]( account_object& obj ){ obj.set_proxy(pxy0); } ); BOOST_REQUIRE( old_size + 1 == ao.size< account_index >() ); - const account_object& obj1 = ao.create( "name01" ); ao.modify( obj1, [&]( account_object& obj ){ obj.proxy = "proxy01"; } ); + const account_object& obj1 = ao.create( "name01" ); ao.modify( obj1, [&]( account_object& obj ){ obj.set_proxy(pxy1); } ); BOOST_REQUIRE( old_size + 2 == ao.size< account_index >() ); ao.remember_old_values< account_index >(); @@ -157,12 +161,10 @@ BOOST_AUTO_TEST_CASE( undo_object_disappear ) Method 'generic_index::modify' works incorrectly - after 'contraint violation', element is removed. Solution: It's necessary to write fix, according to issue #2154. + Status: + Done */ - //Temporary. After fix, this line should be enabled. - HIVE_REQUIRE_THROW( ao.modify( obj1, [&]( account_object& obj ){ obj.name = "name00"; obj.proxy = "proxy00"; } ), boost::exception ); - - //Temporary. After fix, this line should be removed. - //ao.modify( obj1, [&]( account_object& obj ){ obj.name = "nameXYZ"; obj.proxy = "proxyXYZ"; } ); + HIVE_REQUIRE_THROW( ao.modify( obj1, [&]( account_object& obj ){ obj.name = "name00"; obj.set_proxy(pxy0); } ), boost::exception ); udb.undo_end(); BOOST_REQUIRE( old_size + 2 == ao.size< account_index >() ); @@ -281,12 +283,10 @@ BOOST_AUTO_TEST_CASE( undo_different_indexes ) undo_scenario< account_object > ao( *db ); undo_scenario< comment_object > co( *db ); undo_scenario< comment_cashout_object > co_cashout( *db ); - undo_scenario< comment_content_object > cc( *db ); uint32_t old_size_ao; uint32_t old_size_co; uint32_t old_size_co_cashout; - uint32_t old_size_cc; BOOST_TEST_MESSAGE( "--- 2 objects( different types )" ); ao.remember_old_values< account_index >(); @@ -318,11 +318,11 @@ BOOST_AUTO_TEST_CASE( undo_different_indexes ) old_size_co_cashout = co.size< comment_cashout_index >(); udb.undo_begin(); - ao.create( "name00" ); + const account_object& pxy = ao.create( "name00" ); const account_object& obja1 = ao.create( "name01" ); const account_object& obja2 = ao.create( "name02" ); BOOST_REQUIRE( old_size_ao + 3 == ao.size< account_index >() ); - ao.modify( obja1, [&]( account_object& obj ){ obj.proxy = "proxy01"; } ); + ao.modify( obja1, [&]( account_object& obj ){ obj.set_proxy(pxy); } ); ao.remove( obja2 ); BOOST_REQUIRE( old_size_ao + 2 == ao.size< account_index >() ); @@ -338,50 +338,40 @@ BOOST_AUTO_TEST_CASE( undo_different_indexes ) BOOST_REQUIRE( co.check< comment_index >() ); BOOST_REQUIRE( co_cashout.check< comment_cashout_index >() ); - BOOST_TEST_MESSAGE( "--- 4 objects: 'account_object' + 'comment_object' + 'comment_cashout_object' + 'comment_content_object'" ); + BOOST_TEST_MESSAGE( "--- 3 objects: 'account_object' + 'comment_object' + 'comment_cashout_object'" ); BOOST_TEST_MESSAGE( "--- 'account_object' - ( obj A )create" ); BOOST_TEST_MESSAGE( "--- 'comment_object' - ( obj A )create/remove." ); BOOST_TEST_MESSAGE( "--- 'comment_cashout_object' - ( obj A )create/remove." ); - BOOST_TEST_MESSAGE( "--- 'comment_content_object' - ( obj A )create/remove." ); ao.remember_old_values< account_index >(); co.remember_old_values< comment_index >(); co_cashout.remember_old_values< comment_cashout_index >(); - cc.remember_old_values< comment_content_index >(); old_size_ao = ao.size< account_index >(); old_size_co = co.size< comment_index >(); old_size_co_cashout = co_cashout.size< comment_cashout_index >(); - old_size_cc = cc.size< comment_content_index >(); udb.undo_begin(); ao.create( "name01" ); const comment_object& objc2 = co.create( fake_account_object, "12", fake_parent_comment ); const comment_cashout_object& objc2_cashout = co_cashout.create( objc2, fake_account_object, "12", time_point_sec( 10 ), time_point_sec( 20 ) ); - const comment_content_object& objcc1 = cc.create( [&]( comment_content_object& obj ){ obj.comment = comment_object::id_type( 13 ); } ); BOOST_REQUIRE( old_size_ao + 1 == ao.size< account_index >() ); BOOST_REQUIRE( old_size_co + 1 == co.size< comment_index >() ); BOOST_REQUIRE( old_size_co_cashout + 1 == co_cashout.size< comment_cashout_index >() ); - BOOST_REQUIRE( old_size_cc + 1 == cc.size< comment_content_index >() ); co_cashout.modify( objc2_cashout, [&]( comment_cashout_object& obj ){ obj.active = time_point_sec( 21 ); } ); - cc.remove( objcc1 ); BOOST_REQUIRE( old_size_ao + 1 == ao.size< account_index >() ); BOOST_REQUIRE( old_size_co + 1 == co.size< comment_index >() ); BOOST_REQUIRE( old_size_co_cashout + 1 == co_cashout.size< comment_cashout_index >() ); - BOOST_REQUIRE( old_size_cc == cc.size< comment_content_index >() ); udb.undo_end(); BOOST_REQUIRE( ao.check< account_index >() ); BOOST_REQUIRE( co.check< comment_index >() ); - BOOST_REQUIRE( cc.check< comment_content_index >() ); - BOOST_TEST_MESSAGE( "--- 4 objects: 'account_object' + 'comment_object' + 'comment_cashout_object' + 'comment_content_object'" ); + BOOST_TEST_MESSAGE( "--- 3 objects: 'account_object' + 'comment_object' + 'comment_cashout_object'" ); BOOST_TEST_MESSAGE( "--- Creating is before 'undo_start'." ); BOOST_TEST_MESSAGE( "--- 'account_object' - create/5*modify" ); BOOST_TEST_MESSAGE( "--- 'comment_object' - create/remove" ); BOOST_TEST_MESSAGE( "--- 'comment_cashout_object' - create/5*modify/remove" ); - BOOST_TEST_MESSAGE( "--- 'comment_content_object' - create/remove" ); - const comment_content_object& cc1 = cc.create( [&]( comment_content_object& obj ){ obj.comment = comment_object::id_type( 0 ); } ); const comment_object& co1 = co.create( fake_account_object, "12", fake_parent_comment ); const comment_cashout_object& co1_cashout = co_cashout.create( co1, fake_account_object, "12", time_point_sec( 10 ), time_point_sec( 20 ) ); const account_object& ao1 = ao.create( std::to_string(0) ); @@ -389,11 +379,9 @@ BOOST_AUTO_TEST_CASE( undo_different_indexes ) ao.remember_old_values< account_index >(); co.remember_old_values< comment_index >(); co_cashout.remember_old_values< comment_cashout_index >(); - cc.remember_old_values< comment_content_index >(); old_size_ao = ao.size< account_index >(); old_size_co = co.size< comment_index >(); old_size_co_cashout = co_cashout.size< comment_cashout_index >(); - old_size_cc = cc.size< comment_content_index >(); udb.undo_begin(); for( int32_t i=1; i<=5; ++i ) @@ -405,18 +393,13 @@ BOOST_AUTO_TEST_CASE( undo_different_indexes ) BOOST_REQUIRE( old_size_co_cashout = co_cashout.size< comment_cashout_index >() ); } - cc.remove( cc1 ); - BOOST_REQUIRE( old_size_cc - 1 == cc.size< comment_content_index >() ); - udb.undo_end(); BOOST_REQUIRE( old_size_ao == ao.size< account_index >() ); BOOST_REQUIRE( old_size_co == co.size< comment_index >() ); BOOST_REQUIRE( old_size_co_cashout == co_cashout.size< comment_cashout_index >() ); - BOOST_REQUIRE( old_size_cc == cc.size< comment_content_index >() ); BOOST_REQUIRE( ao.check< account_index >() ); BOOST_REQUIRE( co.check< comment_index >() ); BOOST_REQUIRE( co_cashout.check< comment_cashout_index >() ); - BOOST_REQUIRE( cc.check< comment_content_index >() ); } FC_LOG_AND_RETHROW() }