diff --git a/.gitignore b/.gitignore index ed3fc0ac4444..737b17b62002 100644 --- a/.gitignore +++ b/.gitignore @@ -35,8 +35,8 @@ config.log config.status configure libtool -src/config/dash-config.h -src/config/dash-config.h.in +src/config/absolute-config.h +src/config/absolute-config.h.in src/config/stamp-h1 share/setup.nsi share/qt/Info.plist @@ -82,6 +82,7 @@ libconftest.dylib* Makefile absolute-qt Absolute-Qt.app +!/depends/Makefile # Unit-tests Makefile.test @@ -108,7 +109,7 @@ linux-coverage-build linux-build win32-build qa/pull-tester/tests_config.py - +qa/pull-tester/tests_config.ini qa/cache/* diff --git a/.travis.yml b/.travis.yml index 306479ae9ce9..bb66d054a805 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,101 +1,122 @@ # errata: -# - A travis bug causes caches to trample eachother when using the same -# compiler key (which we don't use anyway). This is worked around for now by -# replacing the "compilers" with a build name prefixed by the no-op ":" -# command. See: https://github.com/travis-ci/travis-ci/issues/4393 # - sudo/dist/group are set so as to get Blue Box VMs, necessary for [loopback] # IPv6 support sudo: required -dist: trusty -group: deprecated-2017Q3 +dist: bionic os: linux language: generic +services: + - docker + cache: - apt: true ccache: true directories: - - depends/built - - depends/sdk-sources - - $HOME/.ccache + - $HOME/cache env: global: - - MAKEJOBS=-j5 - - RUN_TESTS=false - - CHECK_DOC=0 - - BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID - - CCACHE_SIZE=400M - - CCACHE_TEMPDIR=/tmp/.ccache-temp - - CCACHE_COMPRESS=1 - - BASE_OUTDIR=$TRAVIS_BUILD_DIR/out - - SDK_URL=https://bitcoincore.org/depends-sources/sdks - - PYTHON_DEBUG=1 - - WINEDEBUG=fixme-all + # DOCKER_HUB_USER + - secure: "absolutetestuser" + # DOCKER_HUB_PASSWORD + - secure: "absolutetestuser" + - DOCKER_BUILD=false + +stages: + - build depends + - build src + - run tests + - build docker + +builddepends: &builddepends + stage: build depends + script: + - $DOCKER_RUN_IN_BUILDER ./ci/build_depends.sh + +buildsrc: &buildsrc + stage: build src + script: + - $DOCKER_RUN_IN_BUILDER ./ci/build_depends.sh + - $DOCKER_RUN_IN_BUILDER ./ci/build_src.sh + +runtests: &runtests + stage: run tests + script: + - $DOCKER_RUN_IN_BUILDER ./ci/build_depends.sh + - $DOCKER_RUN_IN_BUILDER ./ci/build_src.sh + - $DOCKER_RUN_IN_BUILDER ./ci/test_unittests.sh + - $DOCKER_RUN_IN_BUILDER ./ci/test_integrationtests.sh --jobs=3 - matrix: -# ARM - - HOST=arm-linux-gnueabihf PPA="ppa:bitcoin/bitcoin" PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" -# Win32 - - HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PPA="ppa:bitcoin/bitcoin" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.7 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" MAKEJOBS="-j4" -# 32-bit + absolute - - HOST=i686-pc-linux-gnu PPA="ppa:bitcoin/bitcoin" PACKAGES="g++-multilib bc python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/absolute" PYZMQ=true -# Win64 - - HOST=x86_64-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PPA="ppa:bitcoin/bitcoin" PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine1.7 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" MAKEJOBS="-j4" -# absoluted - - HOST=x86_64-unknown-linux-gnu PPA="ppa:bitcoin/bitcoin" PACKAGES="bc python3-zmq" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports" CPPFLAGS="-DDEBUG_LOCKORDER -DENABLE_ABSOLUTE_DEBUG" PYZMQ=true -# No wallet - - HOST=x86_64-unknown-linux-gnu PPA="ppa:bitcoin/bitcoin" PACKAGES="python3" DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" -# Cross-Mac - - HOST=x86_64-apple-darwin11 PPA="ppa:bitcoin/bitcoin" PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" OSX_SDK=10.11 GOAL="deploy" +builddocker: &builddocker + stage: build docker + script: + # no need to run tests again here + - if [ "$DOCKER_BUILD" = "true" ]; then $DOCKER_RUN_IN_BUILDER ./ci/build_depends.sh && $DOCKER_RUN_IN_BUILDER ./ci/build_src.sh && BUILD_DIR=build-ci/absolutecore-$BUILD_TARGET ./docker/build-docker.sh; fi + +jobs: + include: + # build depends + - <<: *builddepends + env: BUILD_TARGET=arm-linux + - <<: *builddepends + env: BUILD_TARGET=win64 + - <<: *builddepends + env: BUILD_TARGET=linux64 + - <<: *builddepends + env: BUILD_TARGET=linux64_release DOCKER_BUILD=true + - <<: *builddepends + env: BUILD_TARGET=mac + # build source + - <<: *buildsrc + env: BUILD_TARGET=arm-linux + - <<: *buildsrc + env: BUILD_TARGET=win64 + - <<: *buildsrc + env: BUILD_TARGET=linux64 + - <<: *buildsrc + env: BUILD_TARGET=linux64_release DOCKER_BUILD=true + - <<: *buildsrc + env: BUILD_TARGET=mac + # run tests (no tests for arm-linux and mac) + - <<: *runtests + env: BUILD_TARGET=win64 + - <<: *runtests + env: BUILD_TARGET=linux64 + - <<: *runtests + env: BUILD_TARGET=linux64_release DOCKER_BUILD=true + # build docker + - <<: *builddocker + env: BUILD_TARGET=linux64_release DOCKER_BUILD=true + +before_cache: + # Save builder image + - docker save absolute-builder-$BUILD_TARGET-$TRAVIS_JOB_NUMBER $(docker history -q absolute-builder-$BUILD_TARGET-$TRAVIS_JOB_NUMBER | grep -v \) | gzip -2 > $HOME/cache/docker/absolute-builder-$BUILD_TARGET.tar.gz before_install: + - travis_retry travis_apt_get_update + - travis_retry sudo apt-get -yq --no-install-suggests --no-install-recommends install docker-ce - - git clone https://github.com/absolute-community/absolute_hash - - travis_retry sudo apt-get install python3-pip python3-dev - - travis_retry sudo add-apt-repository ppa:ubuntu-wine/ppa -y - - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") - - chmod +rwx -R depends/ - - chmod +rwx -R qa/ - - chmod +rwx -R src/ - - chmod +rwx -R share/ - - chmod +rwx -R contrib/ - - chmod +rwx autogen.sh install: - - if [ "$PYZMQ" = "true" ]; then pip3 install pyzmq --user ; fi - - if [ -n "$PPA" ]; then travis_retry sudo add-apt-repository "$PPA" -y; fi - - if [ -n "$DPKG_ADD_ARCH" ]; then sudo dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi - - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi - - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi - - cd absolute_hash && python3 setup.py install --user && cd .. -before_script: - - unset CC; unset CXX - - unset DISPLAY - #- if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi TODO reenable after all Bitcoin PRs have been merged and docs fully fixed - - mkdir -p depends/SDKs depends/sdk-sources - - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - - if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - - make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS -script: + # Our scripts try to be Travis agnostic + - export PULL_REQUEST="$TRAVIS_PULL_REQUEST" + - export JOB_NUMBER="$TRAVIS_JOB_NUMBER" + - export HOST_SRC_DIR=$TRAVIS_BUILD_DIR + - export HOST_CACHE_DIR=$HOME/cache - export TRAVIS_COMMIT_LOG=`git log --format=fuller -1` - - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi - - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST - - BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" - - depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE - - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh - - mkdir build && cd build - - ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) - - make distdir VERSION=$HOST - - cd absolutecore-$HOST - - ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) - - make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL V=1 ; false ) - - export LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib - - if [ "$RUN_TESTS" = "true" ]; then travis_wait 30 make $MAKEJOBS check VERBOSE=1; fi - - if [ "$RUN_TESTS" = "true" ]; then qa/pull-tester/rpc-tests.py --coverage; fi - - cd ../.. - - if [ "$DOCKER_BUILD" = "true" ]; then BUILD_DIR=build/absolutecore-$HOST ./docker/build-docker.sh; fi + - source ./ci/matrix.sh + - mkdir -p $HOST_CACHE_DIR/docker && mkdir -p $HOST_CACHE_DIR/ccache && mkdir -p $HOST_CACHE_DIR/depends && mkdir -p $HOST_CACHE_DIR/sdk-sources + # Keep this as it makes caching related debugging easier + - ls -lah $HOST_CACHE_DIR && ls -lah $HOST_CACHE_DIR/depends && ls -lah $HOST_CACHE_DIR/ccache && ls -lah $HOST_CACHE_DIR/docker + # Load cached builder image + - if [ -f $HOST_CACHE_DIR/docker/absolute-builder-$BUILD_TARGET.tar.gz ]; then zcat $HOST_CACHE_DIR/docker/absolute-builder-$BUILD_TARGET.tar.gz | docker load || true; fi + - travis_retry docker pull ubuntu:bionic + - travis_retry docker build -t $BUILDER_IMAGE_NAME --build-arg=USER_ID=$UID --build-arg=GROUP_ID=$UID --build-arg=BUILD_TARGET=$BUILD_TARGET -f ci/Dockerfile.builder ci +before_script: + # Make sure stdout is in blocking mode. Otherwise builds will fail due to large writes to stdout + # See https://github.com/travis-ci/travis-ci/issues/4704. If this gets fixed, this line can also be removed. + - python3 -c 'import os,sys,fcntl; flags = fcntl.fcntl(sys.stdout, fcntl.F_GETFL); fcntl.fcntl(sys.stdout, fcntl.F_SETFL, flags&~os.O_NONBLOCK);' after_script: - echo $TRAVIS_COMMIT_RANGE - echo $TRAVIS_COMMIT_LOG diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3fa189bd0db5..f868a582f507 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -173,7 +173,7 @@ In general, all pull requests must: the project (for example refactoring for modularisation); - be well peer reviewed; - have unit tests and functional tests where appropriate; - - follow code style guidelines; + - follow [code style guidelines](/doc/developer-notes.md); - not break the existing test suite; - where bugs are fixed, where possible, there should be unit tests demonstrating the bug and also proving the fix. This helps prevent regression. diff --git a/COPYING b/COPYING index 8476a95eccc6..2b7191e93413 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ The MIT License (MIT) -Copyright (c) 2009-2019 The Bitcoin Core developers -Copyright (c) 2014-2019 The Dash Core developers -Copyright (c) 2019 The Absolute Core developers +Copyright (c) 2009-2020 The Bitcoin Core developers +Copyright (c) 2014-2020 The Dash Core developers +Copyright (c) 2020 The Absolute Core developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000000..8c7f76770a5d --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,92 @@ +// This Jenkinsfile will build a builder image and then run the actual build and tests inside this image +// It's very important to not execute any scripts outside of the builder container, as it's our protection against +// external developers bringing in harmful code into Jenkins. +// Jenkins will only run the build if this Jenkinsfile was not modified in an external pull request. Only branches +// which are part of the Absolute repo will allow modification to the Jenkinsfile. + +def targets = [ + 'win32', + 'win64', + 'linux32', + 'linux64', + 'linux64_nowallet', + 'linux64_release', + 'mac', +] + +def tasks = [:] +for(int i = 0; i < targets.size(); i++) { + def target = targets[i] + + tasks["${target}"] = { + node { + def BUILD_NUMBER = sh(returnStdout: true, script: 'echo $BUILD_NUMBER').trim() + def BRANCH_NAME = sh(returnStdout: true, script: 'echo $BRANCH_NAME').trim() + def UID = sh(returnStdout: true, script: 'id -u').trim() + def HOME = sh(returnStdout: true, script: 'echo $HOME').trim() + def pwd = sh(returnStdout: true, script: 'pwd').trim() + + checkout scm + + // restore cache + try { + copyArtifacts(projectName: "absolute-community-absolute/${BRANCH_NAME}", optional: true, selector: lastSuccessful(), filter: "ci-cache-${target}.tar.gz") + } catch (Exception e) { + } + if (fileExists("ci-cache-${target}.tar.gz")) { + hasCache = true + echo "Using cache from absolute-community-absolute/${BRANCH_NAME}" + } else { + try { + copyArtifacts(projectName: 'absolute-community-absolute/develop', optional: true, selector: lastSuccessful(), filter: "ci-cache-${target}.tar.gz"); + } catch (Exception e) { + } + if (fileExists("ci-cache-${target}.tar.gz")) { + hasCache = true + echo "Using cache from absolute-community-absolute/develop" + } + } + + def env = [ + "BUILD_TARGET=${target}", + "PULL_REQUEST=false", + "JOB_NUMBER=${BUILD_NUMBER}", + ] + withEnv(env) { + def builderImageName="absolute-builder-${target}" + + def builderImage + stage("${target}/builder-image") { + builderImage = docker.build("${builderImageName}", "--build-arg BUILD_TARGET=${target} ci -f ci/Dockerfile.builder") + } + + if (hasCache) { + sh "cd ${pwd} && tar xzfv ci-cache-${target}.tar.gz" + } else { + sh "mkdir -p ${pwd}/ci-cache-${target}" + } + + builderImage.inside("-t") { + stage("${target}/depends") { + sh './ci/build_depends.sh' + } + stage("${target}/build") { + sh './ci/build_src.sh' + } + stage("${target}/test") { + sh './ci/test_unittests.sh' + } + stage("${target}/test") { + sh './ci/test_integrationtests.sh' + } + } + + // archive cache + sh "tar czfv ci-cache-${target}.tar.gz ci-cache-${target}" + archiveArtifacts artifacts: "ci-cache-${target}.tar.gz", fingerprint: true + } + } + } +} + +parallel tasks diff --git a/Jenkinsfile.gitian b/Jenkinsfile.gitian new file mode 100644 index 000000000000..f3b8f599e80a --- /dev/null +++ b/Jenkinsfile.gitian @@ -0,0 +1,124 @@ +def targets = [ + 'linux', + 'win', + 'osx', +] + +def osslTarUrl = 'http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz' +def osslPatchUrl = 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch' +def SDK_URL='https://bitcoincore.org/depends-sources/sdks' +def OSX_SDK='10.11' +def proc = 4 +def mem = 2000 + +def repositoryUrl = "https://github.com/absolute-community/absolute.git" + + +def tasks = [:] +for(int i = 0; i < targets.size(); i++) { + def target = targets[i] + + tasks["${target}"] = { + node { + deleteDir() // cleanup workspace + + def pwd = sh(returnStdout: true, script: 'pwd').trim() + def dockerGid = sh(returnStdout: true, script: "stat -c '%g' /var/run/docker.sock").trim() + def BRANCH_NAME = sh(returnStdout: true, script: 'echo $BRANCH_NAME').trim() + def commit = BRANCH_NAME + def hasCache = false + + def gitianDescriptor + + stage("${target}/prepare") { + dir('absolute') { + checkout scm + gitianDescriptor = readYaml file: "contrib/gitian-descriptors/gitian-${target}.yml" + } + dir('gitian-builder') { + git url: 'https://github.com/absolute-community/gitian-builder.git' + } + sh "mkdir -p absolutecore-binaries" + if (target == "osx") { + dir('gitian-builder') { + sh 'mkdir -p inputs' + sh "curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o inputs/MacOSX${OSX_SDK}.sdk.tar.gz" + } + } + + // restore cache + try { + copyArtifacts(projectName: "absolute-community-absolute/${BRANCH_NAME}", optional: true, selector: lastSuccessful(), filter: "cache-${gitianDescriptor.name}.tar.gz") + } catch (Exception e) { + } + if (fileExists("cache-${gitianDescriptor.name}.tar.gz")) { + hasCache = true + echo "Using cache from dashpay-dash-gitian-nightly/${BRANCH_NAME}" + } else { + try { + copyArtifacts(projectName: 'dashpay-dash-gitian-nightly/develop', optional: true, selector: lastSuccessful(), filter: "cache-${gitianDescriptor.name}.tar.gz"); + } catch (Exception e) { + } + if (fileExists("cache-${gitianDescriptor.name}.tar.gz")) { + hasCache = true + echo "Using cache from absolute-community-absolute/develop" + } + } + } + + def gitianImage + stage("${target}/builder-image") { + dir('absolute') { + gitianImage = docker.build("absolute-gitian:${env.BUILD_ID}", 'ci -f ci/Dockerfile.gitian-builder') + } + } + + gitianImage.inside("--group-add ${dockerGid} -t -v \"/var/run/docker.sock:/var/run/docker.sock\"") { + sh "mkdir -p gitian-builder/cache" + if (hasCache) { + sh "cd gitian-builder/cache && tar xzfv ../../cache-${gitianDescriptor.name}.tar.gz" + } + + stage("${target}/download") { + dir('gitian-builder') { + sh "mkdir -p inputs" + sh "cd inputs && curl -R -O ${osslPatchUrl}" + sh "cd inputs && curl -R -O ${osslTarUrl}" + sh "make -C ../absolute/depends download SOURCES_PATH=`pwd`/cache/common" + } + } + stage("${target}/base-vm") { + dir('gitian-builder') { + sh "./bin/make-base-vm --suite bionic --arch amd64 --docker" + } + } + + stage("${target}/gbuild") { + dir('gitian-builder') { + // make sure an old version is not running + sh "docker rm -fv gitian-target || true" + + try { + sh """ + tail -F var/install.log & + tail -F var/build.log & + USE_DOCKER=1 ./bin/gbuild -j ${proc} -m ${mem} --commit absolute=${commit} --url absolute=${repositoryUrl} ../absolute/contrib/gitian-descriptors/gitian-${target}.yml + """ + } finally { + // make sure it doesn't run forever + sh "docker rm -fv gitian-target || true" + } + sh "mv build/out/absolutecore-* ../absolutecore-binaries/" + sh "mv build/out/src/absolutecore-* ../absolutehcore-binaries/" + } + archiveArtifacts artifacts: 'absolutecore-binaries/*', fingerprint: true + } + + sh "cd gitian-builder/cache && tar czfv ../../cache-${gitianDescriptor.name}.tar.gz common ${gitianDescriptor.name}" + archiveArtifacts artifacts: "cache-${gitianDescriptor.name}.tar.gz", fingerprint: true + } + } + } +} + +parallel tasks diff --git a/README.md b/README.md index 6ab76966f719..a5b3d5f7c23f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Absolute 0.12.2.5a +# Absolute 0.12.3.1 Absolute (ABS) is a digital currency inspired by solutions first used in Dash. diff --git a/build_msvc/bitcoin_config.h b/build_msvc/bitcoin_config.h new file mode 100644 index 000000000000..8ce08f9e14af --- /dev/null +++ b/build_msvc/bitcoin_config.h @@ -0,0 +1,347 @@ +// Copyright (c) 2018-2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_BITCOIN_CONFIG_H +#define BITCOIN_BITCOIN_CONFIG_H + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Version Build */ +#define CLIENT_VERSION_BUILD 0 + +/* Version is release */ +#define CLIENT_VERSION_IS_RELEASE false + +/* Major version */ +#define CLIENT_VERSION_MAJOR 0 + +/* Minor version */ +#define CLIENT_VERSION_MINOR 20 + +/* Build revision */ +#define CLIENT_VERSION_REVISION 99 + +/* Copyright holder(s) before %s replacement */ +#define COPYRIGHT_HOLDERS "The %s developers" + +/* Copyright holder(s) */ +#define COPYRIGHT_HOLDERS_FINAL "The Bitcoin Core developers" + +/* Replacement for %s in copyright holders string */ +#define COPYRIGHT_HOLDERS_SUBSTITUTION "Bitcoin Core" + +/* Copyright year */ +#define COPYRIGHT_YEAR 2020 + +/* Define to 1 to enable wallet functions */ +#define ENABLE_WALLET 1 + +/* Define to 1 to enable ZMQ functions */ +#define ENABLE_ZMQ 1 + +/* define if the Boost library is available */ +#define HAVE_BOOST /**/ + +/* define if the Boost::Filesystem library is available */ +#define HAVE_BOOST_FILESYSTEM /**/ + +/* define if the Boost::System library is available */ +#define HAVE_BOOST_SYSTEM /**/ + +/* define if the Boost::Thread library is available */ +#define HAVE_BOOST_THREAD /**/ + +/* define if the Boost::Unit_Test_Framework library is available */ +#define HAVE_BOOST_UNIT_TEST_FRAMEWORK /**/ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BYTESWAP_H */ + +/* Define this symbol if the consensus lib has been built */ +#define HAVE_CONSENSUS_LIB 1 + +/* define if the compiler supports basic C++11 syntax */ +#define HAVE_CXX11 1 + +/* Define to 1 if you have the declaration of `be16toh', and to 0 if you + don't. */ +#define HAVE_DECL_BE16TOH 0 + +/* Define to 1 if you have the declaration of `be32toh', and to 0 if you + don't. */ +#define HAVE_DECL_BE32TOH 0 + +/* Define to 1 if you have the declaration of `be64toh', and to 0 if you + don't. */ +#define HAVE_DECL_BE64TOH 0 + +/* Define to 1 if you have the declaration of `bswap_16', and to 0 if you + don't. */ +#define HAVE_DECL_BSWAP_16 0 + +/* Define to 1 if you have the declaration of `bswap_32', and to 0 if you + don't. */ +#define HAVE_DECL_BSWAP_32 0 + +/* Define to 1 if you have the declaration of `bswap_64', and to 0 if you + don't. */ +#define HAVE_DECL_BSWAP_64 0 + +/* Define to 1 if you have the declaration of `daemon', and to 0 if you don't. + */ +#define HAVE_DECL_DAEMON 0 + +/* Define to 1 if you have the declaration of `htobe16', and to 0 if you + don't. */ +#define HAVE_DECL_HTOBE16 0 + +/* Define to 1 if you have the declaration of `htobe32', and to 0 if you + don't. */ +#define HAVE_DECL_HTOBE32 0 + +/* Define to 1 if you have the declaration of `htobe64', and to 0 if you + don't. */ +#define HAVE_DECL_HTOBE64 0 + +/* Define to 1 if you have the declaration of `htole16', and to 0 if you + don't. */ +#define HAVE_DECL_HTOLE16 0 + +/* Define to 1 if you have the declaration of `htole32', and to 0 if you + don't. */ +#define HAVE_DECL_HTOLE32 0 + +/* Define to 1 if you have the declaration of `htole64', and to 0 if you + don't. */ +#define HAVE_DECL_HTOLE64 0 + +/* Define to 1 if you have the declaration of `le16toh', and to 0 if you + don't. */ +#define HAVE_DECL_LE16TOH 0 + +/* Define to 1 if you have the declaration of `le32toh', and to 0 if you + don't. */ +#define HAVE_DECL_LE32TOH 0 + +/* Define to 1 if you have the declaration of `le64toh', and to 0 if you + don't. */ +#define HAVE_DECL_LE64TOH 0 + +/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you + don't. */ +#define HAVE_DECL_STRERROR_R 0 + +/* Define to 1 if you have the declaration of `strnlen', and to 0 if you + don't. */ +#define HAVE_DECL_STRNLEN 1 + +/* Define to 1 if you have the declaration of `__builtin_clz', and to 0 if you + don't. */ +//#define HAVE_DECL___BUILTIN_CLZ 1 + +/* Define to 1 if you have the declaration of `__builtin_clzl', and to 0 if + you don't. */ +//#define HAVE_DECL___BUILTIN_CLZL 1 + +/* Define to 1 if you have the declaration of `__builtin_clzll', and to 0 if + you don't. */ +//#define HAVE_DECL___BUILTIN_CLZLL 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ENDIAN_H */ + +/* Define to 1 if the system has the `dllexport' function attribute */ +#define HAVE_FUNC_ATTRIBUTE_DLLEXPORT 1 + +/* Define to 1 if the system has the `dllimport' function attribute */ +#define HAVE_FUNC_ATTRIBUTE_DLLIMPORT 1 + +/* Define to 1 if the system has the `visibility' function attribute */ +#define HAVE_FUNC_ATTRIBUTE_VISIBILITY 1 + +/* Define this symbol if the BSD getentropy system call is available */ +/* #undef HAVE_GETENTROPY */ + +/* Define this symbol if the BSD getentropy system call is available with + sys/random.h */ +/* #undef HAVE_GETENTROPY_RAND */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define this symbol if you have malloc_info */ +/* #undef HAVE_MALLOC_INFO */ + +/* Define this symbol if you have mallopt with M_ARENA_MAX */ +/* #undef HAVE_MALLOPT_ARENA_MAX */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MINIUPNPC_MINIUPNPC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MINIUPNPC_MINIWGET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MINIUPNPC_UPNPCOMMANDS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MINIUPNPC_UPNPERRORS_H 1 + +/* Define this symbol if you have MSG_DONTWAIT */ +/* #undef HAVE_MSG_DONTWAIT */ + +/* Define this symbol if you have MSG_NOSIGNAL */ +/* #undef HAVE_MSG_NOSIGNAL */ + +/* Define if you have POSIX threads libraries and header files. */ +//#define HAVE_PTHREAD 1 + +/* Have PTHREAD_PRIO_INHERIT. */ +//#define HAVE_PTHREAD_PRIO_INHERIT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror_r' function. */ +/* #undef HAVE_STRERROR_R */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define this symbol if the BSD sysctl(KERN_ARND) is available */ +/* #undef HAVE_SYSCTL_ARND */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_ENDIAN_H */ + +/* Define this symbol if the Linux getrandom system call is available */ +/* #undef HAVE_SYS_GETRANDOM */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_PRCTL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +//#define HAVE_UNISTD_H 1 + +/* Define if the visibility attribute is supported. */ +#define HAVE_VISIBILITY_ATTRIBUTE 1 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "https://github.com/bitcoin/bitcoin/issues" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "Bitcoin Core" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "Bitcoin Core 0.19.99" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "bitcoin" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "https://bitcoincore.org/" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.19.99" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define this symbol if the qt platform is cocoa */ +/* #undef QT_QPA_PLATFORM_COCOA */ + +/* Define this symbol if the minimal qt platform exists */ +#define QT_QPA_PLATFORM_MINIMAL 1 + +/* Define this symbol if the qt platform is windows */ +#define QT_QPA_PLATFORM_WINDOWS 1 + +/* Define this symbol if the qt platform is xcb */ +/* #undef QT_QPA_PLATFORM_XCB */ + +/* Define this symbol if qt plugins are static */ +#define QT_STATICPLUGIN 1 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if strerror_r returns char *. */ +/* #undef STRERROR_R_CHAR_P */ + +/* Define this symbol to build in assembly routines */ +//#define USE_ASM 1 + +/* Define if dbus support should be compiled in */ +/* #undef USE_DBUS */ + +/* Define if QR support should be compiled in */ +//#define USE_QRCODE 1 + +/* UPnP support not compiled if undefined, otherwise value (0 or 1) determines + default state */ +//#define USE_UPNP 0 + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#define _FILE_OFFSET_BITS 64 + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Windows Universal Platform constraints */ +#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) +/* Either a desktop application without API restrictions, or and older system + before these macros were defined. */ + +/* ::wsystem is available */ +#define HAVE_SYSTEM 1 + +#endif // !WINAPI_FAMILY || WINAPI_FAMILY_DESKTOP_APP + +#endif //BITCOIN_BITCOIN_CONFIG_H diff --git a/ci/Dockerfile.builder b/ci/Dockerfile.builder new file mode 100644 index 000000000000..d6612e3d5a00 --- /dev/null +++ b/ci/Dockerfile.builder @@ -0,0 +1,56 @@ +FROM ubuntu:bionic + +# Build and base stuff +# (zlib1g-dev and libssl-dev are needed for the Qt host binary builds, but should not be used by target binaries) +# We split this up into multiple RUN lines as we might need to retry multiple times on Travis. This way we allow better +# cache usage. +RUN apt-get update +RUN apt-get update && apt-get install -y git +RUN apt-get update && apt-get install -y g++ +RUN apt-get update && apt-get install -y autotools-dev libtool m4 automake autoconf pkg-config +RUN apt-get update && apt-get install -y zlib1g-dev libssl1.0-dev curl ccache bsdmainutils cmake +RUN apt-get update && apt-get install -y python3 python3-dev +RUN apt-get update && apt-get install -y python3-pip + +# Python stuff +RUN pip3 install pyzmq # really needed? + +# absolute_hash +RUN git clone https://github.com/absolute-community/absolute_hash +RUN cd absolute_hash && python3 setup.py install + +ARG USER_ID=1000 +ARG GROUP_ID=1000 + +# add user with specified (or default) user/group ids +ENV USER_ID ${USER_ID} +ENV GROUP_ID ${GROUP_ID} +RUN groupadd -g ${GROUP_ID} absolute +RUN useradd -u ${USER_ID} -g absolute -s /bin/bash -m -d /absolute absolute + +# Extra packages +ARG BUILD_TARGET=linux64 +ADD matrix.sh /tmp/matrix.sh +RUN . /tmp/matrix.sh && \ + if [ -n "$DPKG_ADD_ARCH" ]; then dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi && \ + if [ -n "$PACKAGES" ]; then apt-get update && apt-get install -y --no-install-recommends --no-upgrade $PACKAGES; fi + +# Make sure std::thread and friends is available +# Will fail on non-win builds, but we ignore this +RUN \ + update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix; \ + update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix; \ + update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix; \ + update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix; \ + exit 0 + +RUN mkdir /absolute-src && \ + mkdir -p /cache/ccache && \ + mkdir /cache/depends && \ + mkdir /cache/sdk-sources && \ + chown $USER_ID:$GROUP_ID /absolute-src && \ + chown $USER_ID:$GROUP_ID /cache && \ + chown $USER_ID:$GROUP_ID /cache -R +WORKDIR /absolute-src + +USER absolute diff --git a/ci/Dockerfile.gitian-builder b/ci/Dockerfile.gitian-builder new file mode 100644 index 000000000000..5908aa0f4e7a --- /dev/null +++ b/ci/Dockerfile.gitian-builder @@ -0,0 +1,17 @@ +FROM ubuntu:bionic + +RUN apt-get update && apt-get install -y \ + ruby curl make libltdl7 git apache2 apt-cacher-ng python-vm-builder ruby qemu-utils \ + && rm -rf /var/lib/apt/lists + +ARG USER_ID=1000 +ARG GROUP_ID=1000 + +# add user with specified (or default) user/group ids +ENV USER_ID ${USER_ID} +ENV GROUP_ID ${GROUP_ID} +RUN groupadd -g ${GROUP_ID} absolute +RUN useradd -u ${USER_ID} -g absolute -s /bin/bash -m -d /absolute absolute + +WORKDIR /absolute +USER absolute diff --git a/ci/build_depends.sh b/ci/build_depends.sh new file mode 100755 index 000000000000..d4e7cd0e59ca --- /dev/null +++ b/ci/build_depends.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +# This script is executed inside the builder image + +set -e + +source ./ci/matrix.sh + +unset CC; unset CXX +unset DISPLAY + +mkdir -p $CACHE_DIR/depends +mkdir -p $CACHE_DIR/sdk-sources + +ln -s $CACHE_DIR/depends depends/built +ln -s $CACHE_DIR/sdk-sources depends/sdk-sources + +mkdir -p depends/SDKs + +if [ -n "$OSX_SDK" ]; then + if [ ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then + curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz + fi + if [ -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then + tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz + fi +fi + +make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS diff --git a/ci/build_src.sh b/ci/build_src.sh new file mode 100755 index 000000000000..83833d4fc827 --- /dev/null +++ b/ci/build_src.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +# This script is executed inside the builder image + +set -e + +source ./ci/matrix.sh + +unset CC; unset CXX +unset DISPLAY + +export CCACHE_COMPRESS=${CCACHE_COMPRESS:-1} +export CCACHE_SIZE=${CCACHE_SIZE:-400M} + +#if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi TODO reenable after all Bitcoin PRs have been merged and docs fully fixed + +depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE + +if [ -n "$USE_SHELL" ]; then + export CONFIG_SHELL="$USE_SHELL" +fi + +BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$BUILD_DIR/depends/$HOST --bindir=$OUT_DIR/bin --libdir=$OUT_DIR/lib" + +test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh + +rm -rf build-ci +mkdir build-ci +cd build-ci + +../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) +make distdir VERSION=$BUILD_TARGET + +cd absolutecore-$BUILD_TARGET +./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) + +make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && make $GOAL V=1 ; false ) diff --git a/ci/matrix.sh b/ci/matrix.sh new file mode 100755 index 000000000000..15cd2e3198bd --- /dev/null +++ b/ci/matrix.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +# This script is meant to be sourced into the actual build script. It contains the build matrix and will set all +# necessary environment variables for the request build target + +export BUILD_TARGET=${BUILD_TARGET:-linux64} +export PULL_REQUEST=${PULL_REQUEST:-false} +export JOB_NUMBER=${JOB_NUMBER:-1} + +export BUILDER_IMAGE_NAME="absolute-builder-$BUILD_TARGET-$JOB_NUMBER" + +export HOST_SRC_DIR=${HOST_SRC_DIR:-$(pwd)} +export HOST_CACHE_DIR=${HOST_CACHE_DIR:-$(pwd)/ci-cache-$BUILD_TARGET} + +export SRC_DIR=${SRC_DIR:-$HOST_SRC_DIR} +export BUILD_DIR=$SRC_DIR +export OUT_DIR=$BUILD_DIR/out + +export CACHE_DIR=${CACHE_DIR:-$HOST_CACHE_DIR} +export CCACHE_DIR=$CACHE_DIR/ccache + +export DOCKER_RUN_VOLUME_ARGS="-v $HOST_SRC_DIR:$SRC_DIR -v $HOST_CACHE_DIR:$CACHE_DIR" +export DOCKER_RUN_ENV_ARGS="-e SRC_DIR=$SRC_DIR -e CACHE_DIR=$CACHE_DIR -e PULL_REQUEST=$PULL_REQUEST -e JOB_NUMBER=$JOB_NUMBER -e BUILD_TARGET=$BUILD_TARGET" +export DOCKER_RUN_ARGS="$DOCKER_RUN_VOLUME_ARGS $DOCKER_RUN_ENV_ARGS" +export DOCKER_RUN_IN_BUILDER="docker run -t --rm -w $SRC_DIR $DOCKER_RUN_ARGS $BUILDER_IMAGE_NAME" + +# Default values for targets +export GOAL="install" +export SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks} +export PYTHON_DEBUG=1 +export MAKEJOBS="-j4" + +if [ "$BUILD_TARGET" = "arm-linux" ]; then + export HOST=arm-linux-gnueabihf + export PACKAGES="g++-arm-linux-gnueabihf" + export DEP_OPTS="NO_QT=1" + export CHECK_DOC=1 + export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" +elif [ "$BUILD_TARGET" = "win32" ]; then + export HOST=i686-w64-mingw32 + export DPKG_ADD_ARCH="i386" + export DEP_OPTS="NO_QT=1" + export PACKAGES="python3 nsis g++-mingw-w64-i686 wine-stable wine32 bc" + export BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" + export DIRECT_WINE_EXEC_TESTS=true + export RUN_TESTS=false +elif [ "$BUILD_TARGET" = "win64" ]; then + export HOST=x86_64-w64-mingw32 + export DPKG_ADD_ARCH="i386" + export DEP_OPTS="NO_QT=1" + export PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-stable wine64 bc" + export BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" + export DIRECT_WINE_EXEC_TESTS=true + export RUN_TESTS=false + elif [ "$BUILD_TARGET" = "linux32" ]; then + export HOST=i686-pc-linux-gnu + export PACKAGES="g++-multilib bc python3-zmq" + export DEP_OPTS="NO_QT=1" + export BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" + export USE_SHELL="/bin/dash" + export PYZMQ=true + export RUN_TESTS=false +elif [ "$BUILD_TARGET" = "linux64" ]; then + export HOST=x86_64-unknown-linux-gnu + export PACKAGES="bc python3-zmq" + export DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1" + export BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports" + export CPPFLAGS="-DDEBUG_LOCKORDER -DENABLE_ABSOLUTE_DEBUG" + export PYZMQ=true + export RUN_TESTS=false +elif [ "$BUILD_TARGET" = "linux64_nowallet" ]; then + export HOST=x86_64-unknown-linux-gnu + export PACKAGES="python3" + export DEP_OPTS="NO_WALLET=1" + export BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" +elif [ "$BUILD_TARGET" = "linux64_release" ]; then + export HOST=x86_64-unknown-linux-gnu + export PACKAGES="bc python3-zmq" + export DEP_OPTS="NO_QT=1 NO_UPNP=1" + export BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports" + export PYZMQ=true +elif [ "$BUILD_TARGET" = "mac" ]; then + export HOST=x86_64-apple-darwin11 + export PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools" + export BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" + export OSX_SDK=10.11 + export GOAL="deploy" +fi diff --git a/ci/test_integrationtests.sh b/ci/test_integrationtests.sh new file mode 100755 index 000000000000..2813a8ddb75e --- /dev/null +++ b/ci/test_integrationtests.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# This script is executed inside the builder image + +set -e + +source ./ci/matrix.sh + +if [ "$RUN_TESTS" != "true" ]; then + echo "Skipping integration tests" + exit 0 +fi + +export LD_LIBRARY_PATH=$BUILD_DIR/depends/$HOST/lib + +cd build-ci/absolutecore-$BUILD_TARGET + +./qa/pull-tester/rpc-tests.py --coverage diff --git a/ci/test_unittests.sh b/ci/test_unittests.sh new file mode 100755 index 000000000000..8a6471a368d1 --- /dev/null +++ b/ci/test_unittests.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +# This script is executed inside the builder image + +set -e + +source ./ci/matrix.sh + +if [ "$RUN_TESTS" != "true" ]; then + echo "Skipping unit tests" + exit 0 +fi + +# TODO this is not Travis agnostic +export BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID +export LD_LIBRARY_PATH=$BUILD_DIR/depends/$HOST/lib + +export WINEDEBUG=fixme-all +export BOOST_TEST_LOG_LEVEL=test_suite + +cd build-ci/absolutecore-$BUILD_TARGET +if [ "$DIRECT_WINE_EXEC_TESTS" = "true" ]; then + # Inside Docker, binfmt isn't working so we can't trust in make invoking windows binaries correctly + wine ./src/test/test_absolute.exe +else + make $MAKEJOBS check VERBOSE=1 +fi diff --git a/configure.ac b/configure.ac index 5ec437a1cbee..2d77a42d1c81 100644 --- a/configure.ac +++ b/configure.ac @@ -2,8 +2,8 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 12) -define(_CLIENT_VERSION_REVISION, 2) -define(_CLIENT_VERSION_BUILD, 5) +define(_CLIENT_VERSION_REVISION, 3) +define(_CLIENT_VERSION_BUILD, 1) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2018) define(_COPYRIGHT_HOLDERS,[The %s developers]) @@ -53,8 +53,8 @@ case $host in lt_cv_deplibs_check_method="pass_all" ;; esac -dnl Require C++11 compiler (no GNU extensions) -AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory], [nodefault]) +dnl Require C++14 compiler (no GNU extensions) +AX_CXX_COMPILE_STDCXX([14], [noext], [mandatory], [nodefault]) dnl Check if -latomic is required for CHECK_ATOMIC @@ -164,6 +164,12 @@ AC_ARG_ENABLE([glibc-back-compat], [use_glibc_compat=$enableval], [use_glibc_compat=no]) +AC_ARG_ENABLE([threadlocal], + [AS_HELP_STRING([--enable-threadlocal], + [enable features that depend on the c++ thread_local keyword (currently just thread names in debug logs). (default is to enabled if there is platform support and glibc-back-compat is not enabled)])], + [use_thread_local=$enableval], + [use_thread_local=auto]) + AC_ARG_WITH([system-univalue], [AS_HELP_STRING([--with-system-univalue], [Build with system UniValue (default is no)])], @@ -486,6 +492,8 @@ if test x$use_glibc_compat != xno; then [ fdelt_type="long int"]) AC_MSG_RESULT($fdelt_type) AC_DEFINE_UNQUOTED(FDELT_TYPE, $fdelt_type,[parameter and return value type for __fdelt_chk]) + AX_CHECK_LINK_FLAG([[-Wl,--wrap=__divmoddi4]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=__divmoddi4"]) + AX_CHECK_LINK_FLAG([[-Wl,--wrap=log2f]], [COMPAT_LDFLAGS="$COMPAT_LDFLAGS -Wl,--wrap=log2f"]) else AC_SEARCH_LIBS([clock_gettime],[rt]) fi @@ -580,9 +588,48 @@ AC_LINK_IFELSE([AC_LANG_SOURCE([ ] ) -if test x$use_reduce_exports = xyes; then - AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], - [AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])]) +if test "x$use_thread_local" = xyes || { test "x$use_thread_local" = xauto && test "x$use_glibc_compat" = xno; }; then + TEMP_LDFLAGS="$LDFLAGS" + LDFLAGS="$TEMP_LDFLAGS $PTHREAD_CFLAGS" + AC_MSG_CHECKING([for thread_local support]) + AC_LINK_IFELSE([AC_LANG_SOURCE([ + #include + static thread_local int foo = 0; + static void run_thread() { foo++;} + int main(){ + for(int i = 0; i < 10; i++) { std::thread(run_thread).detach();} + return foo; + } + ])], + [ + case $host in + *mingw*) + # mingw32's implementation of thread_local has also been shown to behave + # erroneously under concurrent usage; see: + # https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605 + AC_MSG_RESULT(no) + ;; + *darwin*) + # TODO enable thread_local on later versions of Darwin where it is + # supported (per https://stackoverflow.com/a/29929949) + AC_MSG_RESULT(no) + ;; + *freebsd*) + # FreeBSD's implementation of thread_local is also buggy (per + # https://groups.google.com/d/msg/bsdmailinglist/22ncTZAbDp4/Dii_pII5AwAJ) + AC_MSG_RESULT(no) + ;; + *) + AC_DEFINE(HAVE_THREAD_LOCAL,1,[Define if thread_local is supported.]) + AC_MSG_RESULT(yes) + ;; + esac + ], + [ + AC_MSG_RESULT(no) + ] + ) + LDFLAGS="$TEMP_LDFLAGS" fi LEVELDB_CPPFLAGS= @@ -1085,6 +1132,11 @@ AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) AC_SUBST(RELDFLAGS) +AC_SUBST(DEBUG_CPPFLAGS) +AC_SUBST(WARN_CXXFLAGS) +AC_SUBST(NOWARN_CXXFLAGS) +AC_SUBST(DEBUG_CXXFLAGS) +AC_SUBST(COMPAT_LDFLAGS) AC_SUBST(ERROR_CXXFLAGS) AC_SUBST(HARDENED_CXXFLAGS) AC_SUBST(HARDENED_CPPFLAGS) @@ -1162,7 +1214,7 @@ case ${OS} in ;; esac -echo +echo echo "Options used to compile and link:" echo " with wallet = $enable_wallet" echo " with gui / qt = $bitcoin_enable_qt" @@ -1176,7 +1228,7 @@ echo " with bench = $use_bench" echo " with upnp = $use_upnp" echo " debug enabled = $enable_debug" echo " werror = $enable_werror" -echo +echo echo " target os = $TARGET_OS" echo " build os = $BUILD_OS" echo @@ -1186,4 +1238,4 @@ echo " CPPFLAGS = $CPPFLAGS" echo " CXX = $CXX" echo " CXXFLAGS = $CXXFLAGS" echo " LDFLAGS = $LDFLAGS" -echo +echo diff --git a/contrib/README.md b/contrib/README.md index b8ad40146007..832e789a0b94 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -34,13 +34,12 @@ Notes on getting Gitian builds up and running using KVM. PGP keys used for signing Bitcoin Core [Gitian release](/doc/release-process.md) results. ### [MacDeploy](/contrib/macdeploy) ### -Scripts and notes for Mac builds. - -### [Gitian-build](/contrib/gitian-build.sh) ### +Scripts and notes for Mac builds. +### [Gitian-build](/contrib/gitian-build.py) ### Script for running full Gitian builds. -Test and Verify Tools +Test and Verify Tools --------------------- ### [TestGen](/contrib/testgen) ### diff --git a/contrib/absolute-qt.pro b/contrib/absolute-qt.pro index 9d0e6788c1d4..12a489f9f516 100644 --- a/contrib/absolute-qt.pro +++ b/contrib/absolute-qt.pro @@ -3,7 +3,6 @@ FORMS += \ ../src/qt/forms/addressbookpage.ui \ ../src/qt/forms/askpassphrasedialog.ui \ ../src/qt/forms/coincontroldialog.ui \ - ../src/qt/forms/darksendconfig.ui \ ../src/qt/forms/debugwindow.ui \ ../src/qt/forms/editaddressdialog.ui \ ../src/qt/forms/helpmessagedialog.ui \ diff --git a/contrib/absoluted.bash-completion b/contrib/absoluted.bash-completion index ed45a5be6606..9ae690535240 100644 --- a/contrib/absoluted.bash-completion +++ b/contrib/absoluted.bash-completion @@ -1,5 +1,5 @@ # bash programmable completion for absoluted(1) and absolute-qt(1) -# Copyright (c) 2012-2019 The Bitcoin Core developers +# Copyright (c) 2012-2020 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/contrib/auto_gdb/README.md b/contrib/auto_gdb/README.md new file mode 100644 index 000000000000..ab75e7959449 --- /dev/null +++ b/contrib/auto_gdb/README.md @@ -0,0 +1,45 @@ +# Contents +This directory contains tools to automatically get data about the memory consumption by some objects in dashd process with the help of GDB debugger. + +## dash_dbg.sh +This shell script attaches GDB to the running dashd process (should be built with debug info), executes debug.gdb script and detaches. +By default it uses testnet settings, see script comments to attach it to mainnet dashd. + +## debug.gdb +Contains debugger instructions to execute during attach: loads python code and executes it for the objects we want to investigate. + +## log_size.py +Contains definition of the gdb command log_size. After this script loads it can be called from gdb command line or other gdb scripts. +Command params: +`log_size arg0 arg1` +`arg0` - name of object whose memory will be written in log file +`arg1` - name of the log file +Example: +``` +log_size mnodeman "memlog.txt" +``` + +## used_size.py +Contains definition of the gdb command used_size. After loading of this script it can be called from gdb command line or other gdb scripts. +Command params: +`used_size arg0 arg1` +`arg0` - variable to store memory used by the object +`arg1` - name of object whose memory will be calculated and stored in the first argument +Example: +``` +>(gdb) set $size = 0 +>(gdb) used_size $size mnodeman +>(gdb) p $size +``` + +## stl_containers.py +Contains helper classes to calculate memory used by the STL containers (list, vector, map, set, pair). + +## simple_class_obj.py +Contains a helper class to calculate the memory used by an object as a sum of the memory used by its fields. +All processed objects of such type are listed in the this file, you can add new types you are interested in to this list. +If a type is not listed here, its size is the return of sizeof (except STL containers which are processed in stl_containers.py). + +## common_helpers.py +Several helper functions that are used in other python code. + diff --git a/contrib/auto_gdb/common_helpers.py b/contrib/auto_gdb/common_helpers.py new file mode 100644 index 000000000000..7dcc6c021db6 --- /dev/null +++ b/contrib/auto_gdb/common_helpers.py @@ -0,0 +1,57 @@ +#!/usr/bin/python +# + +try: + import gdb +except ImportError as e: + raise ImportError("This script must be run in GDB: ", str(e)) +import sys +import os +sys.path.append(os.getcwd()) +import stl_containers +import simple_class_obj + +SIZE_OF_INT = 4 +SIZE_OF_BOOL = 1 +SIZE_OF_INT64 = 8 +SIZE_OF_UINT256 = 32 + + +def get_special_type_obj(gobj): + obj_type = gobj.type.strip_typedefs() + if stl_containers.VectorObj.is_this_type(obj_type): + return stl_containers.VectorObj(gobj) + if stl_containers.ListObj.is_this_type(obj_type): + return stl_containers.ListObj(gobj) + if stl_containers.PairObj.is_this_type(obj_type): + return stl_containers.PairObj(gobj) + if stl_containers.MapObj.is_this_type(obj_type): + return stl_containers.MapObj(gobj) + if stl_containers.SetObj.is_this_type(obj_type): + return stl_containers.SetObj(gobj) + if simple_class_obj.SimpleClassObj.is_this_type(obj_type): + return simple_class_obj.SimpleClassObj(gobj) + return False + + +def is_special_type(obj_type): + if stl_containers.VectorObj.is_this_type(obj_type): + return True + if stl_containers.ListObj.is_this_type(obj_type): + return True + if stl_containers.PairObj.is_this_type(obj_type): + return True + if stl_containers.MapObj.is_this_type(obj_type): + return True + if stl_containers.SetObj.is_this_type(obj_type): + return True + if simple_class_obj.SimpleClassObj.is_this_type(obj_type): + return True + return False + + +def get_instance_size(gobj): + obj = get_special_type_obj(gobj) + if not obj: + return gobj.type.sizeof + return obj.get_used_size() diff --git a/contrib/auto_gdb/dash_dbg.sh b/contrib/auto_gdb/dash_dbg.sh new file mode 100644 index 000000000000..ebe3ed54ec9c --- /dev/null +++ b/contrib/auto_gdb/dash_dbg.sh @@ -0,0 +1,4 @@ +#!/bin/bash +# use testnet settings, if you need mainnet, use ~/.dashcore/dashd.pid file instead +dash_pid=$(<~/.dashcore/testnet3/dashd.pid) +sudo gdb -batch -ex "source debug.gdb" dashd ${dash_pid} diff --git a/contrib/auto_gdb/debug.gdb b/contrib/auto_gdb/debug.gdb new file mode 100644 index 000000000000..2c79aa89c49b --- /dev/null +++ b/contrib/auto_gdb/debug.gdb @@ -0,0 +1,12 @@ +set pagination off +source used_size.py +source log_size.py +source test_used_size.gdb +#logsize privateSendClient "memlog.txt" +#logsize privateSendServer "memlog.txt" +#logsize mnodeman "memlog.txt" +logsize mnpayments "memlog.txt" +#logsize instantsend "memlog.txt" +#logsize sporkManager "memlog.txt" +#logsize masternodeSync "memlog.txt" +#logsize governance "memlog.txt" diff --git a/contrib/auto_gdb/log_size.py b/contrib/auto_gdb/log_size.py new file mode 100644 index 000000000000..08eaa9048ccb --- /dev/null +++ b/contrib/auto_gdb/log_size.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +# + +try: + import gdb +except ImportError as e: + raise ImportError("This script must be run in GDB: ", str(e)) +import traceback +import datetime +import sys +import os +sys.path.append(os.getcwd()) +import common_helpers + + +class LogSizeCommand (gdb.Command): + """calc size of the memory used by the object and write it to file""" + + def __init__ (self): + super (LogSizeCommand, self).__init__ ("logsize", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + try: + args = gdb.string_to_argv(arg) + obj = gdb.parse_and_eval(args[0]) + logfile = open(args[1], 'a') + size = common_helpers.get_instance_size(obj) + logfile.write("%s %s: %d\n" % (str(datetime.datetime.now()), args[0], size)) + logfile.close() + except Exception as e: + print(traceback.format_exc()) + raise e + +LogSizeCommand() diff --git a/contrib/auto_gdb/simple_class_obj.py b/contrib/auto_gdb/simple_class_obj.py new file mode 100644 index 000000000000..7aa0166e01ee --- /dev/null +++ b/contrib/auto_gdb/simple_class_obj.py @@ -0,0 +1,58 @@ +#!/usr/bin/python +# + +try: + import gdb +except ImportError as e: + raise ImportError("This script must be run in GDB: ", str(e)) +import sys +import os +sys.path.append(os.getcwd()) +import common_helpers + + +simple_types = ["CMasternode", "CMasternodeVerification", + "CMasternodeBroadcast", "CMasternodePing", + "CMasternodeMan", "CDarksendQueue", "CDarkSendEntry", + "CTransaction", "CMutableTransaction", "CPrivateSendBaseSession", + "CPrivateSendBaseManager", "CPrivateSendClientSession", + "CPrivateSendClientManager", "CPrivateSendServer", "CMasternodePayments", + "CMasternodePaymentVote", "CMasternodeBlockPayees", + "CMasternodePayee", "CInstantSend", "CTxLockRequest", + "CTxLockVote", "CTxLockCandidate", "COutPoint", + "COutPointLock", "CSporkManager", "CMasternodeSync", + "CGovernanceManager", "CRateCheckBuffer", "CGovernanceObject", + "CGovernanceVote", "CGovernanceObjectVoteFile"] + +simple_templates = ["CacheMultiMap", "CacheMap"] + + +class SimpleClassObj: + + def __init__ (self, gobj): + self.obj = gobj + + @classmethod + def is_this_type(cls, obj_type): + str_type = str(obj_type) + if str_type in simple_types: + return True + for templ in simple_templates: + if str_type.find(templ + "<") == 0: + return True + return False + + def get_used_size(self): + size = 0 + fields = self.obj.type.fields() + for f in fields: + # check if it is static field + if not hasattr(f, "bitpos"): + continue + # process base class size + if f.is_base_class: + size += common_helpers.get_instance_size(self.obj.cast(f.type.strip_typedefs())) + continue + # process simple field + size += common_helpers.get_instance_size(self.obj[f.name]) + return size diff --git a/contrib/auto_gdb/stl_containers.py b/contrib/auto_gdb/stl_containers.py new file mode 100644 index 000000000000..cb6763c5e0b1 --- /dev/null +++ b/contrib/auto_gdb/stl_containers.py @@ -0,0 +1,264 @@ +#!/usr/bin/python +# + +try: + import gdb +except ImportError as e: + raise ImportError("This script must be run in GDB: ", str(e)) +import sys +import os +sys.path.append(os.getcwd()) +import common_helpers + + +def find_type(orig, name): + typ = orig.strip_typedefs() + while True: + # Strip cv qualifiers + search = '%s::%s' % (typ.unqualified(), name) + try: + return gdb.lookup_type(search) + except RuntimeError: + pass + # type is not found, try superclass search + field = typ.fields()[0] + if not field.is_base_class: + raise ValueError("Cannot find type %s::%s" % (str(orig), name)) + typ = field.type + + +def get_value_from_aligned_membuf(buf, valtype): + """Returns the value held in a __gnu_cxx::__aligned_membuf.""" + return buf['_M_storage'].address.cast(valtype.pointer()).dereference() + + +def get_value_from_node(node): + valtype = node.type.template_argument(0) + return get_value_from_aligned_membuf(node['_M_storage'], valtype) + + +class VectorObj: + + def __init__ (self, gobj): + self.obj = gobj + + @classmethod + def is_this_type(cls, obj_type): + type_name = str(obj_type) + if type_name.find("std::vector<") == 0: + return True + if type_name.find("std::__cxx11::vector<") == 0: + return True + return False + + def element_type(self): + return self.obj.type.template_argument(0) + + def size(self): + return int(self.obj['_M_impl']['_M_finish'] - + self.obj['_M_impl']['_M_start']) + + def get_used_size(self): + if common_helpers.is_special_type(self.element_type()): + size = self.obj.type.sizeof + item = self.obj['_M_impl']['_M_start'] + finish = self.obj['_M_impl']['_M_finish'] + while item != finish: + elem = item.dereference() + obj = common_helpers.get_special_type_obj(elem) + size += obj.get_used_size() + item = item + 1 + return size + return self.obj.type.sizeof + self.size() * self.element_type().sizeof + + +class ListObj: + + def __init__ (self, gobj): + self.obj = gobj + + @classmethod + def is_this_type(cls, obj_type): + type_name = str(obj_type) + if type_name.find("std::list<") == 0: + return True + if type_name.find("std::__cxx11::list<") == 0: + return True + return False + + def element_type(self): + return self.obj.type.template_argument(0) + + def get_used_size(self): + is_special = common_helpers.is_special_type(self.element_type()) + head = self.obj['_M_impl']['_M_node'] +# nodetype = find_type(self.obj.type, '_Node') + nodetype = head.type + nodetype = nodetype.strip_typedefs().pointer() + current = head['_M_next'] + size = self.obj.type.sizeof + while current != head.address: + if is_special: + elem = current.cast(nodetype).dereference() + size += common_helpers.get_instance_size(elem) + else: + size += self.element_type().sizeof + current = current['_M_next'] + + return size + + +class PairObj: + + def __init__ (self, gobj): + self.obj = gobj + + @classmethod + def is_this_type(cls, obj_type): + type_name = str(obj_type) + if type_name.find("std::pair<") == 0: + return True + if type_name.find("std::__cxx11::pair<") == 0: + return True + return False + + def key_type(self): + return self.obj.type.template_argument(0) + + def value_type(self): + return self.obj.type.template_argument(1) + + def get_used_size(self): + if not common_helpers.is_special_type(self.key_type()) and not common_helpers.is_special_type(self.value_type()): + return self.key_type().sizeof + self.value_type().sizeof + + size = 0 + + if common_helpers.is_special_type(self.key_type()): + obj = common_helpers.get_special_type_obj(self.obj['first']) + size += obj.get_used_size() + else: + size += self.key_type().sizeof + + if common_helpers.is_special_type(self.value_type()): + obj = common_helpers.get_special_type_obj(self.obj['second']) + size += obj.get_used_size() + else: + size += self.value_type().sizeof + + return size + + +class MapObj: + + def __init__ (self, gobj): + self.obj = gobj + self.obj_type = gobj.type + rep_type = find_type(self.obj_type, "_Rep_type") + self.node_type = find_type(rep_type, "_Link_type") + self.node_type = self.node_type.strip_typedefs() + + @classmethod + def is_this_type(cls, obj_type): + type_name = str(obj_type) + if type_name.find("std::map<") == 0: + return True + if type_name.find("std::__cxx11::map<") == 0: + return True + return False + + def key_type(self): + return self.obj_type.template_argument(0).strip_typedefs() + + def value_type(self): + return self.obj_type.template_argument(1).strip_typedefs() + + def size(self): + res = int(self.obj['_M_t']['_M_impl']['_M_node_count']) + return res + + def get_used_size(self): + if not common_helpers.is_special_type(self.key_type()) and not common_helpers.is_special_type(self.value_type()): + return self.obj_type.sizeof + self.size() * (self.key_type().sizeof + self.value_type().sizeof) + if self.size() == 0: + return self.obj_type.sizeof + size = self.obj_type.sizeof + row_node = self.obj['_M_t']['_M_impl']['_M_header']['_M_left'] + for i in range(self.size()): + node_val = row_node.cast(self.node_type).dereference() + pair = get_value_from_node(node_val) + + obj = common_helpers.get_special_type_obj(pair) + size += obj.get_used_size() + + node = row_node + if node.dereference()['_M_right']: + node = node.dereference()['_M_right'] + while node.dereference()['_M_left']: + node = node.dereference()['_M_left'] + else: + parent = node.dereference()['_M_parent'] + while node == parent.dereference()['_M_right']: + node = parent + parent = parent.dereference()['_M_parent'] + if node.dereference()['_M_right'] != parent: + node = parent + row_node = node + return size + + +class SetObj: + + def __init__ (self, gobj): + self.obj = gobj + self.obj_type = gobj.type + rep_type = find_type(self.obj_type, "_Rep_type") + self.node_type = find_type(rep_type, "_Link_type") + self.node_type = self.node_type.strip_typedefs() + + @classmethod + def is_this_type(cls, obj_type): + type_name = str(obj_type) + if type_name.find("std::set<") == 0: + return True + if type_name.find("std::__cxx11::set<") == 0: + return True + return False + + def element_type(self): + return self.obj_type.template_argument(0) + + def size(self): + res = int(self.obj['_M_t']['_M_impl']['_M_node_count']) + return res + + def get_used_size(self): + if not common_helpers.is_special_type(self.element_type()): + return self.obj_type.sizeof + self.size() * self.element_type().sizeof + if self.size() == 0: + return self.obj_type.sizeof + size = self.obj_type.sizeof + row_node = self.obj['_M_t']['_M_impl']['_M_header']['_M_left'] + for i in range(self.size()): + node_val = row_node.cast(self.node_type).dereference() + val = get_value_from_node(node_val) + + obj = common_helpers.get_special_type_obj(val) + size += obj.get_used_size() + + node = row_node + if node.dereference()['_M_right']: + node = node.dereference()['_M_right'] + while node.dereference()['_M_left']: + node = node.dereference()['_M_left'] + else: + parent = node.dereference()['_M_parent'] + while node == parent.dereference()['_M_right']: + node = parent + parent = parent.dereference()['_M_parent'] + if node.dereference()['_M_right'] != parent: + node = parent + row_node = node + return size + + diff --git a/contrib/auto_gdb/test_used_size.gdb b/contrib/auto_gdb/test_used_size.gdb new file mode 100644 index 000000000000..b46d11d2fc2c --- /dev/null +++ b/contrib/auto_gdb/test_used_size.gdb @@ -0,0 +1,5 @@ +define test_used_size + set $size_ext = 0 + usedsize $size_ext $arg0 + p $size_ext +end diff --git a/contrib/auto_gdb/used_size.py b/contrib/auto_gdb/used_size.py new file mode 100644 index 000000000000..5285965210aa --- /dev/null +++ b/contrib/auto_gdb/used_size.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# + +try: + import gdb +except ImportError as e: + raise ImportError("This script must be run in GDB: ", str(e)) +import traceback +import sys +import os +sys.path.append(os.getcwd()) +import common_helpers + + +class UsedSizeCommand (gdb.Command): + """calc size of the memory used by the object""" + + def __init__ (self): + super (UsedSizeCommand, self).__init__ ("usedsize", gdb.COMMAND_USER) + + @classmethod + def assign_value(cls, obj_name, value): + gdb.execute("set " + obj_name + " = " + str(value)) + + @classmethod + def get_type(cls, obj_name): + return gdb.parse_and_eval(obj_name).type + + def invoke(self, arg, from_tty): + try: + args = gdb.string_to_argv(arg) + obj = gdb.parse_and_eval(args[1]) + obj_type = obj.type + print (args[1] + " is " + str(obj_type)) + size = common_helpers.get_instance_size(obj) + UsedSizeCommand.assign_value(args[0], size) + print (size) + + except Exception as e: + print(traceback.format_exc()) + raise e + +UsedSizeCommand() + diff --git a/contrib/debian/copyright b/contrib/debian/copyright index dc083baab062..72347a479644 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -5,7 +5,7 @@ Upstream-Contact: Satoshi Nakamoto Source: https://github.com/bitcoin/bitcoin Files: * -Copyright: 2009-2019, Bitcoin Core Developers +Copyright: 2009-2020, Bitcoin Core Developers License: Expat Comment: The Bitcoin Core Developers encompasses the current developers listed on bitcoin.org, as well as the numerous contributors to the project. diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py index fa1e137587a6..464e2a8a46b6 100644 --- a/contrib/devtools/check-doc.py +++ b/contrib/devtools/check-doc.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,29 +15,29 @@ FOLDER_GREP = 'src' FOLDER_TEST = 'src/test/' -CMD_ROOT_DIR = '`git rev-parse --show-toplevel`/%s' % FOLDER_GREP -CMD_GREP_ARGS = r"egrep -r -I '(map(Multi)?Args(\.count\(|\[)|Get(Bool)?Arg\()\"\-[^\"]+?\"' %s | grep -v '%s'" % (CMD_ROOT_DIR, FOLDER_TEST) -CMD_GREP_DOCS = r"egrep -r -I 'HelpMessageOpt\(\"\-[^\"=]+?(=|\")' %s" % (CMD_ROOT_DIR) +CMD_ROOT_DIR = '`git rev-parse --show-toplevel`/{}'.format(FOLDER_GREP) +CMD_GREP_ARGS = r"egrep -r -I '(map(Multi)?Args(\.count\(|\[)|Get(Bool)?Arg\()\"\-[^\"]+?\"' {} | grep -v '{}'".format(CMD_ROOT_DIR, FOLDER_TEST) +CMD_GREP_DOCS = r"egrep -r -I 'HelpMessageOpt\(\"\-[^\"=]+?(=|\")' {}".format(CMD_ROOT_DIR) REGEX_ARG = re.compile(r'(?:map(?:Multi)?Args(?:\.count\(|\[)|Get(?:Bool)?Arg\()\"(\-[^\"]+?)\"') REGEX_DOC = re.compile(r'HelpMessageOpt\(\"(\-[^\"=]+?)(?:=|\")') # list unsupported, deprecated and duplicate args as they need no documentation SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-blockminsize']) def main(): - used = check_output(CMD_GREP_ARGS, shell=True) - docd = check_output(CMD_GREP_DOCS, shell=True) + used = check_output(CMD_GREP_ARGS, shell=True, universal_newlines=True) + docd = check_output(CMD_GREP_DOCS, shell=True, universal_newlines=True) args_used = set(re.findall(REGEX_ARG,used)) args_docd = set(re.findall(REGEX_DOC,docd)).union(SET_DOC_OPTIONAL) args_need_doc = args_used.difference(args_docd) args_unknown = args_docd.difference(args_used) - print "Args used : %s" % len(args_used) - print "Args documented : %s" % len(args_docd) - print "Args undocumented: %s" % len(args_need_doc) - print args_need_doc - print "Args unknown : %s" % len(args_unknown) - print args_unknown + print("Args used : {}".format(len(args_used))) + print("Args documented : {}".format(len(args_docd))) + print("Args undocumented: {}".format(len(args_need_doc))) + print(args_need_doc) + print("Args unknown : {}".format(len(args_unknown))) + print(args_unknown) exit(len(args_need_doc)) diff --git a/contrib/devtools/clang-format-diff.py b/contrib/devtools/clang-format-diff.py index 7ea49b65e177..ca1bd8854fc6 100644 --- a/contrib/devtools/clang-format-diff.py +++ b/contrib/devtools/clang-format-diff.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # #===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===# # @@ -69,10 +69,10 @@ import argparse import difflib +import io import re import string import subprocess -import StringIO import sys @@ -133,9 +133,9 @@ def main(): ['-lines', str(start_line) + ':' + str(end_line)]) # Reformat files containing changes in place. - for filename, lines in lines_by_file.iteritems(): + for filename, lines in lines_by_file.items(): if args.i and args.verbose: - print 'Formatting', filename + print('Formatting {}'.format(filename)) command = [binary, filename] if args.i: command.append('-i') @@ -143,8 +143,11 @@ def main(): command.append('-sort-includes') command.extend(lines) command.extend(['-style=file', '-fallback-style=none']) - p = subprocess.Popen(command, stdout=subprocess.PIPE, - stderr=None, stdin=subprocess.PIPE) + p = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=None, + stdin=subprocess.PIPE, + universal_newlines=True) stdout, stderr = p.communicate() if p.returncode != 0: sys.exit(p.returncode) @@ -152,11 +155,11 @@ def main(): if not args.i: with open(filename) as f: code = f.readlines() - formatted_code = StringIO.StringIO(stdout).readlines() + formatted_code = io.StringIO(stdout).readlines() diff = difflib.unified_diff(code, formatted_code, filename, filename, '(before formatting)', '(after formatting)') - diff_string = string.join(diff, '') + diff_string = ''.join(diff) if len(diff_string) > 0: sys.stdout.write(diff_string) diff --git a/contrib/devtools/optimize-pngs.py b/contrib/devtools/optimize-pngs.py index 6086533cdee1..32c136f4626e 100755 --- a/contrib/devtools/optimize-pngs.py +++ b/contrib/devtools/optimize-pngs.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2014-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,7 +10,7 @@ import sys import subprocess import hashlib -from PIL import Image +from PIL import Image # pip3 install Pillow def file_hash(filename): '''Return hash of raw file contents''' @@ -26,8 +26,8 @@ def content_hash(filename): pngcrush = 'pngcrush' git = 'git' -folders = ["src/qt/res/movies", "src/qt/res/icons", "src/qt/res/icons/crownium", "src/qt/res/icons/drkblue", "src/qt/res/icons/light", "src/qt/res/icons/trad", "src/qt/res/images", "src/qt/res/images/crownium", "src/qt/res/images/drkblue", "src/qt/res/images/light", "src/qt/res/images/trad", "share/pixmaps"] -basePath = subprocess.check_output([git, 'rev-parse', '--show-toplevel']).rstrip('\n') +folders = ["src/qt/res/movies", "src/qt/res/icons", "src/qt/res/icons/crownium", "src/qt/res/icons/drkblue", "src/qt/res/icons/light", "src/qt/res/icons/light-retro", "src/qt/res/icons/trad", "src/qt/res/images", "src/qt/res/images/crownium", "src/qt/res/images/drkblue", "src/qt/res/images/light", "src/qt/res/images/light-retro", "src/qt/res/images/trad", "share/pixmaps"] +basePath = subprocess.check_output([git, 'rev-parse', '--show-toplevel'], universal_newlines=True).rstrip('\n') totalSaveBytes = 0 noHashChange = True @@ -37,42 +37,40 @@ def content_hash(filename): for file in os.listdir(absFolder): extension = os.path.splitext(file)[1] if extension.lower() == '.png': - print("optimizing "+file+"..."), + print("optimizing {}...".format(file), end =' ') file_path = os.path.join(absFolder, file) fileMetaMap = {'file' : file, 'osize': os.path.getsize(file_path), 'sha256Old' : file_hash(file_path)} fileMetaMap['contentHashPre'] = content_hash(file_path) - - pngCrushOutput = "" + try: - pngCrushOutput = subprocess.check_output( - [pngcrush, "-brute", "-ow", "-rem", "gAMA", "-rem", "cHRM", "-rem", "iCCP", "-rem", "sRGB", "-rem", "alla", "-rem", "text", file_path], - stderr=subprocess.STDOUT).rstrip('\n') + subprocess.call([pngcrush, "-brute", "-ow", "-rem", "gAMA", "-rem", "cHRM", "-rem", "iCCP", "-rem", "sRGB", "-rem", "alla", "-rem", "text", file_path], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except: - print "pngcrush is not installed, aborting..." + print("pngcrush is not installed, aborting...") sys.exit(0) - + #verify - if "Not a PNG file" in subprocess.check_output([pngcrush, "-n", "-v", file_path], stderr=subprocess.STDOUT): - print "PNG file "+file+" is corrupted after crushing, check out pngcursh version" + if "Not a PNG file" in subprocess.check_output([pngcrush, "-n", "-v", file_path], stderr=subprocess.STDOUT, universal_newlines=True): + print("PNG file "+file+" is corrupted after crushing, check out pngcursh version") sys.exit(1) - + fileMetaMap['sha256New'] = file_hash(file_path) fileMetaMap['contentHashPost'] = content_hash(file_path) if fileMetaMap['contentHashPre'] != fileMetaMap['contentHashPost']: - print "Image contents of PNG file "+file+" before and after crushing don't match" + print("Image contents of PNG file {} before and after crushing don't match".format(file)) sys.exit(1) fileMetaMap['psize'] = os.path.getsize(file_path) outputArray.append(fileMetaMap) - print("done\n"), + print("done") -print "summary:\n+++++++++++++++++" +print("summary:\n+++++++++++++++++") for fileDict in outputArray: oldHash = fileDict['sha256Old'] newHash = fileDict['sha256New'] totalSaveBytes += fileDict['osize'] - fileDict['psize'] noHashChange = noHashChange and (oldHash == newHash) - print fileDict['file']+"\n size diff from: "+str(fileDict['osize'])+" to: "+str(fileDict['psize'])+"\n old sha256: "+oldHash+"\n new sha256: "+newHash+"\n" - -print "completed. Checksum stable: "+str(noHashChange)+". Total reduction: "+str(totalSaveBytes)+" bytes" + print(fileDict['file']+"\n size diff from: "+str(fileDict['osize'])+" to: "+str(fileDict['psize'])+"\n old sha256: "+oldHash+"\n new sha256: "+newHash+"\n") + +print("completed. Checksum stable: "+str(noHashChange)+". Total reduction: "+str(totalSaveBytes)+" bytes") diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index c90541e27119..aa73bc61d0ec 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,7 +8,6 @@ Otherwise the exit status will be 1 and it will log which executables failed which checks. Needs `readelf` (for ELF) and `objdump` (for PE). ''' -from __future__ import division,print_function,unicode_literals import subprocess import sys import os @@ -21,38 +20,38 @@ def check_ELF_PIE(executable): ''' Check for position independent executable (PIE), allowing for address space randomization. ''' - p = subprocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') ok = False - for line in stdout.split(b'\n'): + for line in stdout.splitlines(): line = line.split() - if len(line)>=2 and line[0] == b'Type:' and line[1] == b'DYN': + if len(line)>=2 and line[0] == 'Type:' and line[1] == 'DYN': ok = True return ok def get_ELF_program_headers(executable): '''Return type and flags for ELF program headers''' - p = subprocess.Popen([READELF_CMD, '-l', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen([READELF_CMD, '-l', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') in_headers = False count = 0 headers = [] - for line in stdout.split(b'\n'): - if line.startswith(b'Program Headers:'): + for line in stdout.splitlines(): + if line.startswith('Program Headers:'): in_headers = True - if line == b'': + if line == '': in_headers = False if in_headers: if count == 1: # header line - ofs_typ = line.find(b'Type') - ofs_offset = line.find(b'Offset') - ofs_flags = line.find(b'Flg') - ofs_align = line.find(b'Align') + ofs_typ = line.find('Type') + ofs_offset = line.find('Offset') + ofs_flags = line.find('Flg') + ofs_align = line.find('Align') if ofs_typ == -1 or ofs_offset == -1 or ofs_flags == -1 or ofs_align == -1: raise ValueError('Cannot parse elfread -lW output') elif count > 1: @@ -69,9 +68,9 @@ def check_ELF_NX(executable): have_wx = False have_gnu_stack = False for (typ, flags) in get_ELF_program_headers(executable): - if typ == b'GNU_STACK': + if typ == 'GNU_STACK': have_gnu_stack = True - if b'W' in flags and b'E' in flags: # section is both writable and executable + if 'W' in flags and 'E' in flags: # section is both writable and executable have_wx = True return have_gnu_stack and not have_wx @@ -88,17 +87,17 @@ def check_ELF_RELRO(executable): # However, the dynamic linker need to write to this area so these are RW. # Glibc itself takes care of mprotecting this area R after relocations are finished. # See also http://permalink.gmane.org/gmane.comp.gnu.binutils/71347 - if typ == b'GNU_RELRO': + if typ == 'GNU_RELRO': have_gnu_relro = True have_bindnow = False - p = subprocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') - for line in stdout.split(b'\n'): + for line in stdout.splitlines(): tokens = line.split() - if len(tokens)>1 and tokens[1] == b'(BIND_NOW)' or (len(tokens)>2 and tokens[1] == b'(FLAGS)' and b'BIND_NOW' in tokens[2]): + if len(tokens)>1 and tokens[1] == '(BIND_NOW)' or (len(tokens)>2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2]): have_bindnow = True return have_gnu_relro and have_bindnow @@ -106,13 +105,13 @@ def check_ELF_Canary(executable): ''' Check for use of stack canary ''' - p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') ok = False - for line in stdout.split(b'\n'): - if b'__stack_chk_fail' in line: + for line in stdout.splitlines(): + if '__stack_chk_fail' in line: ok = True return ok @@ -122,13 +121,13 @@ def get_PE_dll_characteristics(executable): Returns a tuple (arch,bits) where arch is 'i386:x86-64' or 'i386' and bits is the DllCharacteristics value. ''' - p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') arch = '' bits = 0 - for line in stdout.split('\n'): + for line in stdout.splitlines(): tokens = line.split() if len(tokens)>=2 and tokens[0] == 'architecture:': arch = tokens[1].rstrip(',') diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index 8f8685006e11..c945fcbde94d 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2014 Wladimir J. van der Laan # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,7 +11,6 @@ find ../gitian-builder/build -type f -executable | xargs python contrib/devtools/symbol-check.py ''' -from __future__ import division, print_function, unicode_literals import subprocess import re import sys @@ -47,28 +46,28 @@ # Ignore symbols that are exported as part of every executable IGNORE_EXPORTS = { -b'_edata', b'_end', b'_init', b'__bss_start', b'_fini', b'_IO_stdin_used' +'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr' } READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt') # Allowed NEEDED libraries ALLOWED_LIBRARIES = { # bitcoind and bitcoin-qt -b'libgcc_s.so.1', # GCC base support -b'libc.so.6', # C library -b'libpthread.so.0', # threading -b'libanl.so.1', # DNS resolve -b'libm.so.6', # math library -b'librt.so.1', # real-time (clock) -b'ld-linux-x86-64.so.2', # 64-bit dynamic linker -b'ld-linux.so.2', # 32-bit dynamic linker +'libgcc_s.so.1', # GCC base support +'libc.so.6', # C library +'libpthread.so.0', # threading +'libanl.so.1', # DNS resolve +'libm.so.6', # math library +'librt.so.1', # real-time (clock) +'ld-linux-x86-64.so.2', # 64-bit dynamic linker +'ld-linux.so.2', # 32-bit dynamic linker # bitcoin-qt only -b'libX11-xcb.so.1', # part of X11 -b'libX11.so.6', # part of X11 -b'libxcb.so.1', # part of X11 -b'libfontconfig.so.1', # font support -b'libfreetype.so.6', # font parsing -b'libdl.so.2' # programming interface to dynamic linker +'libX11-xcb.so.1', # part of X11 +'libX11.so.6', # part of X11 +'libxcb.so.1', # part of X11 +'libfontconfig.so.1', # font support +'libfreetype.so.6', # font parsing +'libdl.so.2' # programming interface to dynamic linker } class CPPFilt(object): @@ -78,10 +77,10 @@ class CPPFilt(object): Use a pipe to the 'c++filt' command. ''' def __init__(self): - self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True) def __call__(self, mangled): - self.proc.stdin.write(mangled + b'\n') + self.proc.stdin.write(mangled + '\n') self.proc.stdin.flush() return self.proc.stdout.readline().rstrip() @@ -95,43 +94,43 @@ def read_symbols(executable, imports=True): Parse an ELF executable and return a list of (symbol,version) tuples for dynamic, imported symbols. ''' - p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip())) syms = [] - for line in stdout.split(b'\n'): + for line in stdout.splitlines(): line = line.split() - if len(line)>7 and re.match(b'[0-9]+:$', line[0]): - (sym, _, version) = line[7].partition(b'@') - is_import = line[6] == b'UND' - if version.startswith(b'@'): + if len(line)>7 and re.match('[0-9]+:$', line[0]): + (sym, _, version) = line[7].partition('@') + is_import = line[6] == 'UND' + if version.startswith('@'): version = version[1:] if is_import == imports: syms.append((sym, version)) return syms def check_version(max_versions, version): - if b'_' in version: - (lib, _, ver) = version.rpartition(b'_') + if '_' in version: + (lib, _, ver) = version.rpartition('_') else: lib = version ver = '0' - ver = tuple([int(x) for x in ver.split(b'.')]) + ver = tuple([int(x) for x in ver.split('.')]) if not lib in max_versions: return False return ver <= max_versions[lib] def read_libraries(filename): - p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) (stdout, stderr) = p.communicate() if p.returncode: raise IOError('Error opening file') libraries = [] - for line in stdout.split(b'\n'): + for line in stdout.splitlines(): tokens = line.split() - if len(tokens)>2 and tokens[1] == b'(NEEDED)': - match = re.match(b'^Shared library: \[(.*)\]$', b' '.join(tokens[2:])) + if len(tokens)>2 and tokens[1] == '(NEEDED)': + match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:])) if match: libraries.append(match.group(1)) else: @@ -145,18 +144,18 @@ def read_libraries(filename): # Check imported symbols for sym,version in read_symbols(filename, True): if version and not check_version(MAX_VERSIONS, version): - print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym).decode('utf-8'), version.decode('utf-8'))) + print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version)) retval = 1 # Check exported symbols for sym,version in read_symbols(filename, False): if sym in IGNORE_EXPORTS: continue - print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym).decode('utf-8'))) + print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym))) retval = 1 # Check dependency libraries for library_name in read_libraries(filename): if library_name not in ALLOWED_LIBRARIES: - print('%s: NEEDED library %s is not allowed' % (filename, library_name.decode('utf-8'))) + print('%s: NEEDED library %s is not allowed' % (filename, library_name)) retval = 1 exit(retval) diff --git a/contrib/devtools/update-translations.py b/contrib/devtools/update-translations.py index a61c19c1cdf2..5a4d49aff4c5 100755 --- a/contrib/devtools/update-translations.py +++ b/contrib/devtools/update-translations.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2014 Wladimir J. van der Laan # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,7 +15,6 @@ TODO: - auto-add new translations to the build system according to the translation process ''' -from __future__ import division, print_function import subprocess import re import sys diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py new file mode 100644 index 000000000000..70569702178f --- /dev/null +++ b/contrib/gitian-build.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 + +import argparse +import os +import subprocess +import sys + +def setup(): + global args, workdir + programs = ['ruby', 'git', 'make', 'wget'] + if args.lxc: + programs += ['apt-cacher-ng', 'lxc', 'debootstrap'] + elif args.kvm: + programs += ['apt-cacher-ng', 'python-vm-builder', 'qemu-kvm', 'qemu-utils'] + elif args.docker and not os.path.isfile('/lib/systemd/system/docker.service'): + dockers = ['docker.io', 'docker-ce'] + for i in dockers: + return_code = subprocess.call(['sudo', 'apt-get', 'install', '-qq', i]) + if return_code == 0: + break + if return_code != 0: + print('Cannot find any way to install Docker', file=sys.stderr) + exit(1) + subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs) + if not os.path.isdir('gitian.sigs'): + subprocess.check_call(['git', 'clone', 'https://github.com/dashpay/gitian.sigs.git']) + if not os.path.isdir('dash-detached-sigs'): + subprocess.check_call(['git', 'clone', 'https://github.com/dashpay/dash-detached-sigs.git']) + if not os.path.isdir('gitian-builder'): + subprocess.check_call(['git', 'clone', 'https://github.com/devrandom/gitian-builder.git']) + if not os.path.isdir('dash'): + subprocess.check_call(['git', 'clone', 'https://github.com/dashpay/dash.git']) + os.chdir('gitian-builder') + make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64'] + if args.docker: + make_image_prog += ['--docker'] + elif args.lxc: + make_image_prog += ['--lxc'] + subprocess.check_call(make_image_prog) + os.chdir(workdir) + if args.is_bionic and not args.kvm and not args.docker: + subprocess.check_call(['sudo', 'sed', '-i', 's/lxcbr0/br0/', '/etc/default/lxc-net']) + print('Reboot is required') + exit(0) + +def build(): + global args, workdir + + os.makedirs('dashcore-binaries/' + args.version, exist_ok=True) + print('\nBuilding Dependencies\n') + os.chdir('gitian-builder') + os.makedirs('inputs', exist_ok=True) + + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz']) + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch']) + subprocess.check_output(["echo 'a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 inputs/osslsigncode-Backports-to-1.7.1.patch' | sha256sum -c"], shell=True) + subprocess.check_output(["echo 'f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 inputs/osslsigncode-1.7.1.tar.gz' | sha256sum -c"], shell=True) + subprocess.check_call(['make', '-C', '../dash/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common']) + + if args.linux: + print('\nCompiling ' + args.version + ' Linux') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'dash='+args.commit, '--url', 'dash='+args.url, '../dash/contrib/gitian-descriptors/gitian-linux.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../gitian.sigs/', '../dash/contrib/gitian-descriptors/gitian-linux.yml']) + subprocess.check_call('mv build/out/dashcore-*.tar.gz build/out/src/dashcore-*.tar.gz ../dashcore-binaries/'+args.version, shell=True) + + if args.windows: + print('\nCompiling ' + args.version + ' Windows') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'dash='+args.commit, '--url', 'dash='+args.url, '../dash/contrib/gitian-descriptors/gitian-win.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-unsigned', '--destination', '../gitian.sigs/', '../dash/contrib/gitian-descriptors/gitian-win.yml']) + subprocess.check_call('mv build/out/dashcore-*-win-unsigned.tar.gz inputs/dashcore-win-unsigned.tar.gz', shell=True) + subprocess.check_call('mv build/out/dashcore-*.zip build/out/dashcore-*.exe ../dashcore-binaries/'+args.version, shell=True) + + if args.macos: + print('\nCompiling ' + args.version + ' MacOS') + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/depends-sources/sdks/MacOSX10.11.sdk.tar.gz']) + subprocess.check_output(["echo 'bec9d089ebf2e2dd59b1a811a38ec78ebd5da18cbbcd6ab39d1e59f64ac5033f inputs/MacOSX10.11.sdk.tar.gz' | sha256sum -c"], shell=True) + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'dash='+args.commit, '--url', 'dash='+args.url, '../dash/contrib/gitian-descriptors/gitian-osx.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-unsigned', '--destination', '../gitian.sigs/', '../dash/contrib/gitian-descriptors/gitian-osx.yml']) + subprocess.check_call('mv build/out/dashcore-*-osx-unsigned.tar.gz inputs/dashcore-osx-unsigned.tar.gz', shell=True) + subprocess.check_call('mv build/out/dashcore-*.tar.gz build/out/dashcore-*.dmg ../dashcore-binaries/'+args.version, shell=True) + + os.chdir(workdir) + + if args.commit_files: + print('\nCommitting '+args.version+' Unsigned Sigs\n') + os.chdir('gitian.sigs') + subprocess.check_call(['git', 'add', args.version+'-linux/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-win-unsigned/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-osx-unsigned/'+args.signer]) + subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer]) + os.chdir(workdir) + +def sign(): + global args, workdir + os.chdir('gitian-builder') + + if args.windows: + print('\nSigning ' + args.version + ' Windows') + subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../dash/contrib/gitian-descriptors/gitian-win-signer.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-signed', '--destination', '../gitian.sigs/', '../dash/contrib/gitian-descriptors/gitian-win-signer.yml']) + subprocess.check_call('mv build/out/dashcore-*win64-setup.exe ../dashcore-binaries/'+args.version, shell=True) + subprocess.check_call('mv build/out/dashcore-*win32-setup.exe ../dashcore-binaries/'+args.version, shell=True) + + if args.macos: + print('\nSigning ' + args.version + ' MacOS') + subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../dash/contrib/gitian-descriptors/gitian-osx-signer.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-signed', '--destination', '../gitian.sigs/', '../dash/contrib/gitian-descriptors/gitian-osx-signer.yml']) + subprocess.check_call('mv build/out/dashcore-osx-signed.dmg ../dashcore-binaries/'+args.version+'/dashcore-'+args.version+'-osx.dmg', shell=True) + + os.chdir(workdir) + + if args.commit_files: + print('\nCommitting '+args.version+' Signed Sigs\n') + os.chdir('gitian.sigs') + subprocess.check_call(['git', 'add', args.version+'-win-signed/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-osx-signed/'+args.signer]) + subprocess.check_call(['git', 'commit', '-a', '-m', 'Add '+args.version+' signed binary sigs for '+args.signer]) + os.chdir(workdir) + +def verify(): + global args, workdir + os.chdir('gitian-builder') + + print('\nVerifying v'+args.version+' Linux\n') + subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-linux', '../dash/contrib/gitian-descriptors/gitian-linux.yml']) + print('\nVerifying v'+args.version+' Windows\n') + subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-unsigned', '../dash/contrib/gitian-descriptors/gitian-win.yml']) + print('\nVerifying v'+args.version+' MacOS\n') + subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-unsigned', '../dash/contrib/gitian-descriptors/gitian-osx.yml']) + print('\nVerifying v'+args.version+' Signed Windows\n') + subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-signed', '../dash/contrib/gitian-descriptors/gitian-win-signer.yml']) + print('\nVerifying v'+args.version+' Signed MacOS\n') + subprocess.call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-signed', '../dash/contrib/gitian-descriptors/gitian-osx-signer.yml']) + + os.chdir(workdir) + +def main(): + global args, workdir + + parser = argparse.ArgumentParser(usage='%(prog)s [options] signer version') + parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch') + parser.add_argument('-p', '--pull', action='store_true', dest='pull', help='Indicate that the version argument is the number of a github repository pull request') + parser.add_argument('-u', '--url', dest='url', default='https://github.com/dashpay/dash', help='Specify the URL of the repository. Default is %(default)s') + parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build') + parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build') + parser.add_argument('-s', '--sign', action='store_true', dest='sign', help='Make signed binaries for Windows and MacOS') + parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries') + parser.add_argument('-o', '--os', dest='os', default='lwm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, w for Windows, m for MacOS') + parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s') + parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s') + parser.add_argument('-V', '--virtualization', dest='virtualization', default='docker', help='Specify virtualization technology to use: lxc for LXC, kvm for KVM, docker for Docker. Default is %(default)s') + parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Only works on Debian-based systems (Ubuntu, Debian)') + parser.add_argument('-D', '--detach-sign', action='store_true', dest='detach_sign', help='Create the assert file for detached signing. Will not commit anything.') + parser.add_argument('-n', '--no-commit', action='store_false', dest='commit_files', help='Do not commit anything to git') + parser.add_argument('signer', help='GPG signer to sign each build assert file') + parser.add_argument('version', help='Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified') + + args = parser.parse_args() + workdir = os.getcwd() + + args.linux = 'l' in args.os + args.windows = 'w' in args.os + args.macos = 'm' in args.os + + args.is_bionic = b'bionic' in subprocess.check_output(['lsb_release', '-cs']) + + if args.buildsign: + args.build = True + args.sign = True + + args.sign_prog = 'true' if args.detach_sign else 'gpg --detach-sign' + + args.lxc = (args.virtualization == 'lxc') + args.kvm = (args.virtualization == 'kvm') + args.docker = (args.virtualization == 'docker') + + script_name = os.path.basename(sys.argv[0]) + # Set all USE_* environment variables for gitian-builder: USE_LXC, USE_DOCKER and USE_VBOX + os.environ['USE_VBOX'] = '' + if args.lxc: + os.environ['USE_LXC'] = '1' + os.environ['USE_DOCKER'] = '' + if 'GITIAN_HOST_IP' not in os.environ.keys(): + os.environ['GITIAN_HOST_IP'] = '10.0.3.1' + if 'LXC_GUEST_IP' not in os.environ.keys(): + os.environ['LXC_GUEST_IP'] = '10.0.3.5' + elif args.kvm: + os.environ['USE_LXC'] = '' + os.environ['USE_DOCKER'] = '' + elif args.docker: + os.environ['USE_LXC'] = '' + os.environ['USE_DOCKER'] = '1' + else: + print(script_name+': Wrong virtualization option.') + print('Try '+script_name+' --help for more information') + exit(1) + + # Signer and version shouldn't be empty + if args.signer == '': + print(script_name+': Missing signer.') + print('Try '+script_name+' --help for more information') + exit(1) + if args.version == '': + print(script_name+': Missing version.') + print('Try '+script_name+' --help for more information') + exit(1) + + # Add leading 'v' for tags + if args.commit and args.pull: + raise Exception('Cannot have both commit and pull') + args.commit = ('' if args.commit else 'v') + args.version + + if args.setup: + setup() + + if not args.build and not args.sign and not args.verify: + exit(0) + + os.chdir('dash') + if args.pull: + subprocess.check_call(['git', 'fetch', args.url, 'refs/pull/'+args.version+'/merge']) + os.chdir('../gitian-builder/inputs/dash') + subprocess.check_call(['git', 'fetch', args.url, 'refs/pull/'+args.version+'/merge']) + args.commit = subprocess.check_output(['git', 'show', '-s', '--format=%H', 'FETCH_HEAD'], universal_newlines=True, encoding='utf8').strip() + args.version = 'pull-' + args.version + print(args.commit) + subprocess.check_call(['git', 'fetch']) + subprocess.check_call(['git', 'checkout', args.commit]) + os.chdir(workdir) + + if args.build: + build() + + if args.sign: + sign() + + if args.verify: + verify() + +if __name__ == '__main__': + main() diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh index d05bcf69295a..69b395b61029 100644 --- a/contrib/gitian-build.sh +++ b/contrib/gitian-build.sh @@ -28,6 +28,9 @@ scriptName=$(basename -- "$0") signProg="gpg --detach-sign" commitFiles=true +SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks} +OSX_SDK=${OSX_SDK:-10.11} + # Help Message read -d '' usage <<- EOF Usage: $scriptName [-c|u|v|b|s|B|o|h|j|m|] signer version @@ -258,14 +261,13 @@ then if [[ -n "$USE_LXC" ]] then sudo apt-get install lxc - bin/make-base-vm --suite trusty --arch amd64 --lxc - + bin/make-base-vm --suite bionic --arch amd64 --lxc elif [[ -n "$USE_DOCKER" ]] then sudo apt-get install docker-ce - bin/make-base-vm --suite trusty --arch amd64 --docker + bin/make-base-vm --suite bionic --arch amd64 --docker else - bin/make-base-vm --suite trusty --arch amd64 + bin/make-base-vm --suite bionic --arch amd64 fi popd fi @@ -319,8 +321,15 @@ then echo "" echo "Compiling ${VERSION} Mac OSX" echo "" + + if [ -n "$OSX_SDK" ]; then + if [ ! -f inputs/MacOSX${OSX_SDK}.sdk.tar.gz ]; then + curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o inputs/MacOSX${OSX_SDK}.sdk.tar.gz + fi + fi + ./bin/gbuild -j ${proc} -m ${mem} --commit absolute=${COMMIT} --url absolute=${url} ../absolute/contrib/gitian-descriptors/gitian-osx.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-unsigned --destination ../gitian.signatures/ ../absolute/contrib/gitian-descriptors/gitian-osx.yml + ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../absolute/contrib/gitian-descriptors/gitian-osx.yml mv build/out/absolutecore-*-osx-unsigned.tar.gz inputs/absolutecore-osx-unsigned.tar.gz mv build/out/absolutecore-*.tar.gz build/out/absolutecore-*.dmg ../absolutecore-binaries/${VERSION} fi @@ -377,7 +386,7 @@ fi if [[ $sign = true ]] then - + pushd ./gitian-builder # Sign Windows if [[ $windows = true ]] diff --git a/contrib/gitian-descriptors/README.md b/contrib/gitian-descriptors/README.md index 4f53e113dbcf..a7cf46a02ed4 100644 --- a/contrib/gitian-descriptors/README.md +++ b/contrib/gitian-descriptors/README.md @@ -26,7 +26,7 @@ Once you've got the right hardware and software: # Create base images cd gitian-builder - bin/make-base-vm --suite trusty --arch amd64 + bin/make-base-vm --suite bionic --arch amd64 cd .. # Get inputs (see doc/release-process.md for exact inputs needed and where to get them) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index 4a2a66ba3ae7..ec40038de938 100755 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,32 +1,35 @@ --- -name: "absolute-linux-0.12" +name: "absolute-linux-0.12.3" enable_cache: true suites: -- "trusty" +- "bionic" architectures: - "amd64" packages: - "curl" - "g++-aarch64-linux-gnu" -- "g++-4.8-aarch64-linux-gnu" -- "gcc-4.8-aarch64-linux-gnu" +- "g++-7-aarch64-linux-gnu" +- "gcc-7-aarch64-linux-gnu" - "binutils-aarch64-linux-gnu" - "g++-arm-linux-gnueabihf" -- "g++-4.8-arm-linux-gnueabihf" -- "gcc-4.8-arm-linux-gnueabihf" +- "g++-7-arm-linux-gnueabihf" +- "gcc-7-arm-linux-gnueabihf" - "binutils-arm-linux-gnueabihf" -- "g++-4.8-multilib" -- "gcc-4.8-multilib" +- "g++-7-multilib" +- "gcc-7-multilib" - "binutils-gold" -- "git-core" +- "git" - "pkg-config" - "autoconf" - "libtool" - "automake" +- "cmake" - "faketime" - "bsdmainutils" - "ca-certificates" - "python" +- "python3" +- "libxkbcommon0" remotes: - "url": "https://github.com/absolute-community/absolute.git" "dir": "absolute" @@ -35,7 +38,7 @@ script: | WRAP_DIR=$HOME/wrapped HOSTS="i686-pc-linux-gnu x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu" - CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests" + CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests" FAKETIME_HOST_PROGS="" FAKETIME_PROGS="date ar ranlib nm" HOST_CFLAGS="-O2 -g" @@ -50,18 +53,40 @@ script: | mkdir -p ${WRAP_DIR} if test -n "$GBUILD_CACHE_ENABLED"; then export SOURCES_PATH=${GBUILD_COMMON_CACHE} - export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE}/depends mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + + # Setup ccache to use correct cache directories and fix the compiler check of ccache + CONFIGFLAGS="${CONFIGFLAGS} --enable-ccache" + export CCACHE_DIR=${GBUILD_PACKAGE_CACHE}/ccache + # As we later wrap the gcc binaries, this is fast + export CCACHE_COMPILERCHECK="content" + if [ -f ${GBUILD_PACKAGE_CACHE}/ccache.tar ]; then + pushd ${GBUILD_PACKAGE_CACHE} + tar xf ccache.tar + rm ccache.tar + popd + fi + # instead of compressing ccache.tar, we let ccache handle it by itself + # Otherwise we end up uncompressing/compressing a lot of cache files which we actually never use + export CCACHE_COMPRESS=1 + else + CONFIGFLAGS="${CONFIGFLAGS} --disable-ccache" fi + # We include the GCC version in all wrappers so that ccache can detect compiler upgrades when hashing the wrappers + GCCVERSION=`gcc --version | head -1` + function create_global_faketime_wrappers { for prog in ${FAKETIME_PROGS}; do echo '#!/bin/bash' > ${WRAP_DIR}/${prog} + echo "# GCCVERSION=${GCCVERSION}" >> ${WRAP_DIR}/${prog} echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} echo "\$REAL \$@" >> $WRAP_DIR/${prog} chmod +x ${WRAP_DIR}/${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${prog} done } @@ -69,11 +94,13 @@ script: | for i in $HOSTS; do for prog in ${FAKETIME_HOST_PROGS}; do echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} + echo "# GCCVERSION=${GCCVERSION}" >> ${WRAP_DIR}/${i}-${prog} echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} chmod +x ${WRAP_DIR}/${i}-${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${i}-${prog} done done } @@ -98,6 +125,7 @@ script: | rm -f ${WRAP_DIR}/${prog} cat << EOF > ${WRAP_DIR}/${prog} #!/bin/bash + # GCCVERSION=${GCCVERSION} REAL="`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1`" for var in "\$@" do @@ -110,6 +138,7 @@ script: | \$REAL \$@ EOF chmod +x ${WRAP_DIR}/${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${prog} done cd absolute chmod 777 depends/config.sub @@ -154,8 +183,7 @@ script: | mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST - CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" LDFLAGS="${HOST_LDFLAGS}" - chmod 777 share/genbuild.sh + CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" LDFLAGS="${HOST_LDFLAGS}" make ${MAKEOPTS} make ${MAKEOPTS} -C src check-security #TODO: This is a quick hack that disables symbol checking for arm. @@ -182,3 +210,10 @@ script: | mkdir -p $OUTDIR/src mv $SOURCEDIST $OUTDIR/src + # Compress ccache (otherwise the assert file will get too huge) + if [ "$CCACHE_DIR" != "" ]; then + pushd ${GBUILD_PACKAGE_CACHE} + tar cf ccache.tar ccache + rm -rf ccache + popd + fi diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml index a3ce72233564..e49482bb3a11 100644 --- a/contrib/gitian-descriptors/gitian-osx-signer.yml +++ b/contrib/gitian-descriptors/gitian-osx-signer.yml @@ -1,7 +1,7 @@ --- name: "absolute-dmg-signer" suites: -- "trusty" +- "bionic" architectures: - "amd64" packages: diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index 6161d2b1908a..6387dd21f836 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -1,15 +1,15 @@ --- -name: "absolute-osx-0.12" +name: "absolute-osx-0.12.3" enable_cache: true suites: -- "trusty" +- "bionic" architectures: - "amd64" packages: - "ca-certificates" - "curl" - "g++" -- "git-core" +- "git" - "pkg-config" - "autoconf" - "librsvg2-bin" @@ -25,7 +25,9 @@ packages: - "libbz2-dev" - "python" - "python-dev" -- "python-setuptools" +- "python3" +- "python3-dev" +- "python3-setuptools" - "fonts-tuffy" remotes: - "url": "https://github.com/absolute-community/absolute.git" @@ -47,8 +49,23 @@ script: | mkdir -p ${WRAP_DIR} if test -n "$GBUILD_CACHE_ENABLED"; then export SOURCES_PATH=${GBUILD_COMMON_CACHE} - export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE}/depends mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + + # Setup ccache to use correct cache directories + CONFIGFLAGS="${CONFIGFLAGS} --enable-ccache" + export CCACHE_DIR=${GBUILD_PACKAGE_CACHE}/ccache + if [ -f ${GBUILD_PACKAGE_CACHE}/ccache.tar ]; then + pushd ${GBUILD_PACKAGE_CACHE} + tar xf ccache.tar + rm ccache.tar + popd + fi + # instead of compressing ccache.tar, we let ccache handle it by itself + # Otherwise we end up uncompressing/compressing a lot of cache files which we actually never use + export CCACHE_COMPRESS=1 + else + CONFIGFLAGS="${CONFIGFLAGS} --disable-ccache" fi export ZERO_AR_DATE=1 @@ -61,6 +78,7 @@ script: | echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} echo "\$REAL \$@" >> $WRAP_DIR/${prog} chmod +x ${WRAP_DIR}/${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${prog} done } @@ -73,6 +91,7 @@ script: | echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} chmod +x ${WRAP_DIR}/${i}-${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${i}-${prog} done done } @@ -122,7 +141,7 @@ script: | mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST - CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} + CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} make ${MAKEOPTS} make install-strip DESTDIR=${INSTALLPATH} @@ -154,3 +173,11 @@ script: | mkdir -p $OUTDIR/src mv $SOURCEDIST $OUTDIR/src mv ${OUTDIR}/${DISTNAME}-x86_64-*.tar.gz ${OUTDIR}/${DISTNAME}-osx64.tar.gz + + # Compress ccache (otherwise the assert file will get too huge) + if [ "$CCACHE_DIR" != "" ]; then + pushd ${GBUILD_PACKAGE_CACHE} + tar cf ccache.tar ccache + rm -rf ccache + popd + fi diff --git a/contrib/gitian-descriptors/gitian-rpi2.yml b/contrib/gitian-descriptors/gitian-rpi2.yml deleted file mode 100644 index 20e2fa0a98a7..000000000000 --- a/contrib/gitian-descriptors/gitian-rpi2.yml +++ /dev/null @@ -1,112 +0,0 @@ ---- -name: "absolute-raspberry-0.12" -enable_cache: true -suites: -- "precise" -architectures: -- "amd64" -packages: -- "g++-multilib" -- "git-core" -- "pkg-config" -- "autoconf2.13" -- "libtool" -- "automake" -- "faketime" -- "bsdmainutils" -- "binutils-gold" -reference_datetime: "2017-01-01 00:00:00" -remotes: -- "url": "https://github.com/absolute-community/absolute.git" - "dir": "absolute" -files: -- "raspberrypi-tools.tar.gz" -script: | - WRAP_DIR=$HOME/wrapped - HOSTS="arm-linux-gnueabihf" - CONFIGFLAGS="--enable-upnp-default --enable-glibc-back-compat" - FAKETIME_HOST_PROGS="" - FAKETIME_PROGS="date ar ranlib nm strip" - - tar --warning=no-timestamp -xzf raspberrypi-tools.tar.gz - export TOOLCHAIN_BIN=$(pwd)/raspberrypi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin - export PATH=$PATH:$TOOLCHAIN_BIN - - export QT_RCC_TEST=1 - export GZIP="-9n" - export TAR_OPTIONS="--mtime="$REFERENCE_DATE\\\ $REFERENCE_TIME"" - export TZ="UTC" - export BUILD_DIR=`pwd` - mkdir -p ${WRAP_DIR} - if test -n "$GBUILD_CACHE_ENABLED"; then - export SOURCES_PATH=${GBUILD_COMMON_CACHE} - export BASE_CACHE=${GBUILD_PACKAGE_CACHE} - mkdir -p ${BASE_CACHE} ${SOURCES_PATH} - fi - - # Create global faketime wrappers - for prog in ${FAKETIME_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${prog} - echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} - echo 'export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} - echo "export FAKETIME=\"${REFERENCE_DATETIME}\"" >> ${WRAP_DIR}/${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${prog} - chmod +x ${WRAP_DIR}/${prog} - done - - # Create per-host faketime wrappers - for i in $HOSTS; do - for prog in ${FAKETIME_HOST_PROGS}; do - echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} - echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} - echo 'export LD_PRELOAD=/usr/lib/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} - echo "export FAKETIME=\"${REFERENCE_DATETIME}\"" >> ${WRAP_DIR}/${i}-${prog} - echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} - chmod +x ${WRAP_DIR}/${i}-${prog} - done - done - export PATH=${WRAP_DIR}:${PATH} - - cd absolute - BASEPREFIX=`pwd`/depends - # Build dependencies for each host - for i in $HOSTS; do - make ${MAKEOPTS} NO_QT=1 -C ${BASEPREFIX} HOST="${i}" - done - - # Create the release tarball using (arbitrarily) the first host - ./autogen.sh - ./configure --prefix=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'` - make NO_QT=1 dist - SOURCEDIST=`echo absolutecore-*.tar.gz` - DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` - # Correct tar file order - mkdir -p temp - pushd temp - tar xf ../$SOURCEDIST - find absolutecore-* | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ../$SOURCEDIST - popd - - ORIGPATH="$PATH" - # Extract the release tarball into a dir for each host and build - for i in ${HOSTS}; do - export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} - mkdir -p distsrc-${i} - cd distsrc-${i} - INSTALLPATH=`pwd`/installed/${DISTNAME} - mkdir -p ${INSTALLPATH} - tar --strip-components=1 -xf ../$SOURCEDIST - - ./configure --prefix=${BASEPREFIX}/${i} --bindir=${INSTALLPATH}/bin --includedir=${INSTALLPATH}/include --libdir=${INSTALLPATH}/lib --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} - make ${MAKEOPTS} NO_QT=1 - make NO_QT=1 install-strip - cd installed - find . -name "lib*.la" -delete - find . -name "lib*.a" -delete - rm -rf ${DISTNAME}/lib/pkgconfig - find ${DISTNAME} | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz - cd ../../ - done - mkdir -p $OUTDIR/src - mv $SOURCEDIST $OUTDIR/src - mv ${OUTDIR}/${DISTNAME}-arm-*.tar.gz ${OUTDIR}/${DISTNAME}-RPi2.tar.gz diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml index 81712a5cf123..b8414ef2bb35 100644 --- a/contrib/gitian-descriptors/gitian-win-signer.yml +++ b/contrib/gitian-descriptors/gitian-win-signer.yml @@ -1,7 +1,7 @@ --- name: "absolute-win-signer" suites: -- "trusty" +- "bionic" architectures: - "amd64" packages: diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 2eece37e6af5..13178197b6ef 100755 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -1,18 +1,19 @@ --- -name: "absolute-win-0.12" +name: "absolute-win-0.12.3" enable_cache: true suites: -- "trusty" +- "bionic" architectures: - "amd64" packages: - "curl" - "g++" -- "git-core" +- "git" - "pkg-config" - "autoconf" - "libtool" - "automake" +- "cmake" - "faketime" - "bsdmainutils" - "mingw-w64" @@ -21,6 +22,8 @@ packages: - "zip" - "ca-certificates" - "python" +- "python3" +- "rename" remotes: - "url": "https://github.com/absolute-community/absolute.git" "dir": "absolute" @@ -29,7 +32,7 @@ script: | WRAP_DIR=$HOME/wrapped HOSTS="i686-w64-mingw32 x86_64-w64-mingw32" CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests" - FAKETIME_HOST_PROGS="g++ ar ranlib nm windres strip objcopy" + FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy" FAKETIME_PROGS="date makensis zip" HOST_CFLAGS="-O2 -g" HOST_CXXFLAGS="-O2 -g" @@ -42,18 +45,40 @@ script: | mkdir -p ${WRAP_DIR} if test -n "$GBUILD_CACHE_ENABLED"; then export SOURCES_PATH=${GBUILD_COMMON_CACHE} - export BASE_CACHE=${GBUILD_PACKAGE_CACHE} + export BASE_CACHE=${GBUILD_PACKAGE_CACHE}/depends mkdir -p ${BASE_CACHE} ${SOURCES_PATH} + + # Setup ccache to use correct cache directories and fix the compiler check of ccache + CONFIGFLAGS="${CONFIGFLAGS} --enable-ccache" + export CCACHE_DIR=${GBUILD_PACKAGE_CACHE}/ccache + # As we later wrap the gcc binaries, this is fast + export CCACHE_COMPILERCHECK="content" + if [ -f ${GBUILD_PACKAGE_CACHE}/ccache.tar ]; then + pushd ${GBUILD_PACKAGE_CACHE} + tar xf ccache.tar + rm ccache.tar + popd + fi + # instead of compressing ccache.tar, we let ccache handle it by itself + # Otherwise we end up uncompressing/compressing a lot of cache files which we actually never use + export CCACHE_COMPRESS=1 + else + CONFIGFLAGS="${CONFIGFLAGS} --disable-ccache" fi + # We include the GCC version in all wrappers so that ccache can detect compiler upgrades when hashing the wrappers + GCCVERSION=`gcc --version | head -1` + function create_global_faketime_wrappers { for prog in ${FAKETIME_PROGS}; do echo '#!/bin/bash' > ${WRAP_DIR}/${prog} + echo "# GCCVERSION=${GCCVERSION}" >> ${WRAP_DIR}/${prog} echo "REAL=\`which -a ${prog} | grep -v ${WRAP_DIR}/${prog} | head -1\`" >> ${WRAP_DIR}/${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${prog} echo "\$REAL \$@" >> $WRAP_DIR/${prog} chmod +x ${WRAP_DIR}/${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${prog} done } @@ -61,11 +86,13 @@ script: | for i in $HOSTS; do for prog in ${FAKETIME_HOST_PROGS}; do echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} + echo "# GCCVERSION=${GCCVERSION}" >> ${WRAP_DIR}/${i}-${prog} echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} chmod +x ${WRAP_DIR}/${i}-${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${i}-${prog} done done } @@ -77,19 +104,25 @@ script: | mkdir -p ${WRAP_DIR}/${i} for prog in collect2; do echo '#!/bin/bash' > ${WRAP_DIR}/${i}/${prog} + echo "# GCCVERSION=${GCCVERSION}" >> ${WRAP_DIR}/${i}/${prog} REAL=$(${i}-gcc -print-prog-name=${prog}) echo "export MALLOC_PERTURB_=255" >> ${WRAP_DIR}/${i}/${prog} echo "${REAL} \$@" >> $WRAP_DIR/${i}/${prog} chmod +x ${WRAP_DIR}/${i}/${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${i}/${prog} done for prog in gcc g++; do echo '#!/bin/bash' > ${WRAP_DIR}/${i}-${prog} - echo "REAL=\`which -a ${i}-${prog} | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} + echo "# GCCVERSION=${GCCVERSION}" >> ${WRAP_DIR}/${i}-${prog} + echo "REAL=\`which -a ${i}-${prog}-posix | grep -v ${WRAP_DIR}/${i}-${prog} | head -1\`" >> ${WRAP_DIR}/${i}-${prog} + echo '# Add the gcc version to the wrapper so that ccache takes this into account (we use CCACHE_COMPILERCHECK=content)' >> ${WRAP_DIR}/${i}-${prog} + echo "# `${prog} --version | head -1`" >> ${WRAP_DIR}/${i}-${prog} echo 'export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1' >> ${WRAP_DIR}/${i}-${prog} echo "export FAKETIME=\"$1\"" >> ${WRAP_DIR}/${i}-${prog} echo "export COMPILER_PATH=${WRAP_DIR}/${i}" >> ${WRAP_DIR}/${i}-${prog} echo "\$REAL \$@" >> $WRAP_DIR/${i}-${prog} chmod +x ${WRAP_DIR}/${i}-${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${i}-${prog} done done } @@ -143,8 +176,7 @@ script: | mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST - CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-ccache --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" - chmod 777 share/genbuild.sh + CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CFLAGS="${HOST_CFLAGS}" CXXFLAGS="${HOST_CXXFLAGS}" make ${MAKEOPTS} make ${MAKEOPTS} -C src check-security make deploy @@ -169,3 +201,11 @@ script: | mv ${OUTDIR}/${DISTNAME}-i686-*-debug.zip ${OUTDIR}/${DISTNAME}-win32-debug.zip mv ${OUTDIR}/${DISTNAME}-x86_64-*.zip ${OUTDIR}/${DISTNAME}-win64.zip mv ${OUTDIR}/${DISTNAME}-i686-*.zip ${OUTDIR}/${DISTNAME}-win32.zip + + # Compress ccache (otherwise the assert file will get too huge) + if [ "$CCACHE_DIR" != "" ]; then + pushd ${GBUILD_PACKAGE_CACHE} + tar cf ccache.tar ccache + rm -rf ccache + popd + fi diff --git a/contrib/macdeploy/custom_dsstore.py b/contrib/macdeploy/custom_dsstore.py index ffa32a53b66c..9684b5a67eb5 100644 --- a/contrib/macdeploy/custom_dsstore.py +++ b/contrib/macdeploy/custom_dsstore.py @@ -1,9 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2013-2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from __future__ import division,print_function,unicode_literals import biplist from ds_store import DSStore from mac_alias import Alias diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index f7216c02fb51..a3d16a29f946 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -1,6 +1,4 @@ -#!/usr/bin/env python - -from __future__ import division, print_function, unicode_literals +#!/usr/bin/env python3 # # Copyright (C) 2011 Patrick "p2k" Schneider # @@ -204,7 +202,7 @@ def getFrameworks(binaryPath, verbose): if verbose >= 3: print("Inspecting with otool: " + binaryPath) otoolbin=os.getenv("OTOOL", "otool") - otool = subprocess.Popen([otoolbin, "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + otool = subprocess.Popen([otoolbin, "-L", binaryPath], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) o_stdout, o_stderr = otool.communicate() if otool.returncode != 0: if verbose >= 1: @@ -212,7 +210,7 @@ def getFrameworks(binaryPath, verbose): sys.stderr.flush() raise RuntimeError("otool failed with return code %d" % otool.returncode) - otoolLines = o_stdout.decode().split("\n") + otoolLines = o_stdout.split("\n") otoolLines.pop(0) # First line is the inspected binary if ".framework" in binaryPath or binaryPath.endswith(".dylib"): otoolLines.pop(0) # Frameworks and dylibs list themselves as a dependency. @@ -332,7 +330,7 @@ def deployFrameworks(frameworks, bundlePath, binaryPath, strip, verbose, deploym # Get the Qt path from one of the Qt frameworks if deploymentInfo.qtPath is None and framework.isQtFramework(): deploymentInfo.detectQtPath(framework.frameworkDirectory) - + if framework.installName.startswith("@executable_path") or framework.installName.startswith(bundlePath): if verbose >= 2: print(framework.frameworkName, "already deployed, skipping.") @@ -543,7 +541,7 @@ if len(config.fancy) == 1: if verbose >= 1: sys.stderr.write("Error: Could not import plistlib which is required for fancy disk images.\n") sys.exit(1) - + p = config.fancy[0] if verbose >= 3: print("Fancy: Loading \"%s\"..." % p) @@ -717,22 +715,6 @@ elif config.sign: if config.dmg is not None: - #Patch in check_output for Python 2.6 - if "check_output" not in dir( subprocess ): - def f(*popenargs, **kwargs): - if 'stdout' in kwargs: - raise ValueError('stdout argument not allowed, it will be overridden.') - process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) - output, unused_err = process.communicate() - retcode = process.poll() - if retcode: - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - raise CalledProcessError(retcode, cmd) - return output - subprocess.check_output = f - def runHDIUtil(verb, image_basename, **kwargs): hdiutil_args = ["hdiutil", verb, image_basename + ".dmg"] if "capture_stdout" in kwargs: @@ -750,7 +732,7 @@ if config.dmg is not None: if not value is True: hdiutil_args.append(str(value)) - return run(hdiutil_args) + return run(hdiutil_args, universal_newlines=True) if verbose >= 2: if fancy is None: @@ -792,7 +774,7 @@ if config.dmg is not None: except subprocess.CalledProcessError as e: sys.exit(e.returncode) - m = re.search("/Volumes/(.+$)", output.decode()) + m = re.search("/Volumes/(.+$)", output) disk_root = m.group(0) disk_name = m.group(1) @@ -868,6 +850,7 @@ if config.dmg is not None: print("Running AppleScript:") print(s) + time.sleep(2) # fixes '112:116: execution error: Finder got an error: Can’t get disk "Dash-Core". (-1728)' p = subprocess.Popen(['osascript', '-'], stdin=subprocess.PIPE) p.communicate(input=s.encode('utf-8')) if p.returncode: @@ -876,7 +859,7 @@ if config.dmg is not None: if verbose >= 2: print("+ Finalizing .dmg disk image +") time.sleep(5) - + try: runHDIUtil("convert", dmg_name + ".temp", format="UDBZ", o=dmg_name + ".dmg", ov=True) except subprocess.CalledProcessError as e: diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py index f0c0d3d17141..69900e49f109 100755 --- a/contrib/seeds/makeseeds.py +++ b/contrib/seeds/makeseeds.py @@ -10,7 +10,7 @@ MAX_SEEDS_PER_ASN=4 -MIN_PROTOCOL_VERSION = 70210 +MIN_PROTOCOL_VERSION = 70211 MAX_LAST_SEEN_DIFF = 60 * 60 * 24 * 1 # 1 day MAX_LAST_PAID_DIFF = 60 * 60 * 24 * 30 # 1 month @@ -25,6 +25,7 @@ import collections import json import time +import multiprocessing PATTERN_IPV4 = re.compile(r"^((\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})):(\d+)$") PATTERN_IPV6 = re.compile(r"^\[([0-9a-z:]+)\]:(\d+)$") @@ -90,6 +91,10 @@ def filtermultiport(ips): hist[ip['sortkey']].append(ip) return [value[0] for (key,value) in list(hist.items()) if len(value)==1] +def resolveasn(resolver, ip): + asn = int([x.to_text() for x in resolver.query('.'.join(reversed(ip.split('.'))) + '.origin.asn.cymru.com', 'TXT').response.answer][0].split('\"')[1].split(' ')[0]) + return asn + # Based on Greg Maxwell's seed_filter.py def filterbyasn(ips, max_per_asn, max_total): # Sift out ips by type @@ -99,17 +104,23 @@ def filterbyasn(ips, max_per_asn, max_total): my_resolver = dns.resolver.Resolver() + pool = multiprocessing.Pool(processes=16) + # OpenDNS servers my_resolver.nameservers = ['208.67.222.222', '208.67.220.220'] + # Resolve ASNs in parallel + asns = [pool.apply_async(resolveasn, args=(my_resolver, ip['ip'])) for ip in ips_ipv4] + # Filter IPv4 by ASN result = [] asn_count = {} - for ip in ips_ipv4: + for i in range(len(ips_ipv4)): + ip = ips_ipv4[i] if len(result) == max_total: break try: - asn = int([x.to_text() for x in my_resolver.query('.'.join(reversed(ip['ip'].split('.'))) + '.origin.asn.cymru.com', 'TXT').response.answer][0].split('\"')[1].split(' ')[0]) + asn = asns[i].get() if asn not in asn_count: asn_count[asn] = 0 if asn_count[asn] == max_per_asn: @@ -127,7 +138,11 @@ def filterbyasn(ips, max_per_asn, max_total): return result def main(): - js = json.load(sys.stdin) + if len(sys.argv) > 1: + with open(sys.argv[1], 'r') as f: + js = json.load(f) + else: + js = json.load(sys.stdin) ips = [parseline(line) for collateral, line in js.items()] cur_time = int(time.time()) diff --git a/contrib/seeds/nodes_main.txt b/contrib/seeds/nodes_main.txt index 692ffa1da772..78e7da9aa87b 100644 --- a/contrib/seeds/nodes_main.txt +++ b/contrib/seeds/nodes_main.txt @@ -1,22 +1,6 @@ -addnode=139.99.202.1:18888 -addnode=139.99.41.242:18888 -addnode=139.99.41.241:18888 -addnode=51.255.174.238:18888 -addnode=54.37.14.240:18888 -addnode=164.132.195.79:18888 -addnode=151.80.233.116:18888 -addnode=139.99.96.203:18888 -addnode=139.99.40.157:18888 -addnode=139.99.41.35:18888 -addnode=139.99.41.198:18888 -addnode=139.99.44.0:18888 -addnode=139.99.97.225:18888 -addnode=139.99.105.69:18888 -addnode=139.99.99.113:18888 -addnode=139.99.99.108:18888 -addnode=139.99.99.121:18888 -addnode=139.99.106.75:18888 -addnode=139.99.44.75:18888 -addnode=139.99.44.76:18888 -addnode=139.99.44.77:18888 -addnode=139.99.199.250:18888 +88.198.95.174:18888 +135.181.33.18:18888 +45.77.138.219:18888 +95.216.209.25:18888 +116.203.202.68:18888 + diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py index 56edf292c106..a11d63e2bcb8 100644 --- a/contrib/testgen/base58.py +++ b/contrib/testgen/base58.py @@ -28,7 +28,9 @@ def b58encode(v): """ long_value = 0 for (i, c) in enumerate(v[::-1]): - long_value += (256**i) * ord(c) + if isinstance(c, str): + c = ord(c) + long_value += (256**i) * c result = '' while long_value >= __b58base: @@ -41,7 +43,7 @@ def b58encode(v): # leading 0-bytes in the input become leading-1s nPad = 0 for c in v: - if c == '\0': nPad += 1 + if c == 0: nPad += 1 else: break return (__b58chars[0]*nPad) + result @@ -50,8 +52,10 @@ def b58decode(v, length = None): """ decode v into a string of len bytes """ long_value = 0 - for (i, c) in enumerate(v[::-1]): - long_value += __b58chars.find(c) * (__b58base**i) + for i, c in enumerate(v[::-1]): + pos = __b58chars.find(c) + assert pos != -1 + long_value += pos * (__b58base**i) result = bytes() while long_value >= 256: @@ -62,10 +66,12 @@ def b58decode(v, length = None): nPad = 0 for c in v: - if c == __b58chars[0]: nPad += 1 - else: break + if c == __b58chars[0]: + nPad += 1 + continue + break - result = chr(0)*nPad + result + result = bytes(nPad) + result if length is not None and len(result) != length: return None diff --git a/contrib/testgen/gen_base58_test_vectors.py b/contrib/testgen/gen_base58_test_vectors.py index a0b4b7d20105..ccdcfa5269c1 100755 --- a/contrib/testgen/gen_base58_test_vectors.py +++ b/contrib/testgen/gen_base58_test_vectors.py @@ -1,11 +1,11 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2012 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' Generate valid and invalid base58 address and private key test vectors. -Usage: +Usage: gen_base58_test_vectors.py valid 50 > ../../src/test/data/base58_keys_valid.json gen_base58_test_vectors.py invalid 50 > ../../src/test/data/base58_keys_invalid.json ''' @@ -46,8 +46,8 @@ def is_valid(v): if result is None: return False for template in templates: - prefix = str(bytearray(template[0])) - suffix = str(bytearray(template[2])) + prefix = bytearray(template[0]) + suffix = bytearray(template[2]) if result.startswith(prefix) and result.endswith(suffix): if (len(result) - len(prefix) - len(suffix)) == template[1]: return True @@ -57,20 +57,23 @@ def gen_valid_vectors(): '''Generate valid test vectors''' while True: for template in templates: - prefix = str(bytearray(template[0])) - payload = os.urandom(template[1]) - suffix = str(bytearray(template[2])) + prefix = bytearray(template[0]) + payload = bytearray(os.urandom(template[1])) + suffix = bytearray(template[2]) rv = b58encode_chk(prefix + payload + suffix) assert is_valid(rv) - metadata = dict([(x,y) for (x,y) in zip(metadata_keys,template[3]) if y is not None]) - yield (rv, b2a_hex(payload), metadata) + metadata = {x: y for x, y in zip(metadata_keys,template[3]) if y is not None} + hexrepr = b2a_hex(payload) + if isinstance(hexrepr, bytes): + hexrepr = hexrepr.decode('utf8') + yield (rv, hexrepr, metadata) def gen_invalid_vector(template, corrupt_prefix, randomize_payload_size, corrupt_suffix): '''Generate possibly invalid vector''' if corrupt_prefix: prefix = os.urandom(1) else: - prefix = str(bytearray(template[0])) + prefix = bytearray(template[0]) if randomize_payload_size: payload = os.urandom(max(int(random.expovariate(0.5)), 50)) @@ -80,7 +83,7 @@ def gen_invalid_vector(template, corrupt_prefix, randomize_payload_size, corrupt if corrupt_suffix: suffix = os.urandom(len(template[2])) else: - suffix = str(bytearray(template[2])) + suffix = bytearray(template[2]) return b58encode_chk(prefix + payload + suffix) diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py index 47087e875b59..fa15f1285c4b 100755 --- a/contrib/zmq/zmq_sub.py +++ b/contrib/zmq/zmq_sub.py @@ -14,9 +14,15 @@ zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtxlock") +zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashgovernancevote") +zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashgovernanceobject") +zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashinstantsenddoublespend") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawblock") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtx") zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtxlock") +zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawgovernancevote") +zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawgovernanceobject") +zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawinstantsenddoublespend") zmqSubSocket.connect("tcp://127.0.0.1:%i" % port) try: @@ -48,6 +54,24 @@ elif topic == "rawtxlock": print('- RAW TX LOCK ('+sequence+') -') print(binascii.hexlify(body).decode("utf-8")) + elif topic == "rawinstantsenddoublespend": + print('- RAW IS DOUBLE SPEND ('+sequence+') -') + print(binascii.hexlify(body).decode("utf-8")) + elif topic == "hashgovernancevote": + print('- HASH GOVERNANCE VOTE ('+sequence+') -') + print(binascii.hexlify(body).decode("utf-8")) + elif topic == "hashgovernanceobject": + print('- HASH GOVERNANCE OBJECT ('+sequence+') -') + print(binascii.hexlify(body).decode("utf-8")) + elif topic == "rawgovernancevote": + print('- RAW GOVERNANCE VOTE ('+sequence+') -') + print(binascii.hexlify(body).decode("utf-8")) + elif topic == "rawgovernanceobject": + print('- RAW GOVERNANCE OBJECT ('+sequence+') -') + print(binascii.hexlify(body).decode("utf-8")) + elif topic == "hashinstantsenddoublespend": + print('- HASH IS DOUBLE SPEND ('+sequence+') -') + print(binascii.hexlify(body).decode("utf-8")) except KeyboardInterrupt: zmqContext.destroy() diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index 27f550ab036a..d15d6feea6f4 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -11,7 +11,7 @@ build_darwin_DOWNLOAD = curl --location --fail --connect-timeout $(DOWNLOAD_CONN #darwin host on darwin builder. overrides darwin host preferences. darwin_CC=$(shell xcrun -f clang) -mmacosx-version-min=$(OSX_MIN_VERSION) -darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ +darwin_CXX:=$(shell xcrun -f clang++) -mmacosx-version-min=$(OSX_MIN_VERSION) -stdlib=libc++ -fvisibility=hidden darwin_AR:=$(shell xcrun -f ar) darwin_RANLIB:=$(shell xcrun -f ranlib) darwin_STRIP:=$(shell xcrun -f strip) diff --git a/depends/config.sub b/depends/config.sub index 7e792b4ae17b..b001b2f777f3 100755 --- a/depends/config.sub +++ b/depends/config.sub @@ -1399,7 +1399,7 @@ case $os in | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -miaipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ diff --git a/depends/hosts/linux.mk b/depends/hosts/linux.mk index b13a0f1ad714..602206d63421 100644 --- a/depends/hosts/linux.mk +++ b/depends/hosts/linux.mk @@ -1,5 +1,5 @@ linux_CFLAGS=-pipe -linux_CXXFLAGS=$(linux_CFLAGS) +linux_CXXFLAGS=$(linux_CFLAGS) -static-libstdc++ linux_release_CFLAGS=-O2 linux_release_CXXFLAGS=$(linux_release_CFLAGS) diff --git a/depends/hosts/mingw32.mk b/depends/hosts/mingw32.mk index dbfb62fdcf98..83fc501a1990 100644 --- a/depends/hosts/mingw32.mk +++ b/depends/hosts/mingw32.mk @@ -1,5 +1,5 @@ mingw32_CFLAGS=-pipe -mingw32_CXXFLAGS=$(mingw32_CFLAGS) +mingw32_CXXFLAGS=$(mingw32_CFLAGS) -static-libstdc++ mingw32_release_CFLAGS=-O2 mingw32_release_CXXFLAGS=$(mingw32_release_CFLAGS) diff --git a/depends/packages/chia_bls.mk b/depends/packages/chia_bls.mk new file mode 100644 index 000000000000..e9aa76982c11 --- /dev/null +++ b/depends/packages/chia_bls.mk @@ -0,0 +1,50 @@ +package=chia_bls +$(package)_version=v20181101 +# It's actually from https://github.com/Chia-Network/bls-signatures, but we have so many patches atm that it's forked +$(package)_download_path=https://github.com/codablock/bls-signatures/archive +$(package)_file_name=$($(package)_version).tar.gz +$(package)_sha256_hash=b3ec74a77a7b6795f84b05e051a0824ef8d9e05b04b2993f01040f35689aa87c +$(package)_dependencies=gmp +#$(package)_patches=...TODO (when we switch back to https://github.com/Chia-Network/bls-signatures) + +#define $(package)_preprocess_cmds +# for i in $($(package)_patches); do patch -N -p1 < $($(package)_patch_dir)/$$$$i; done +#endef + +define $(package)_set_vars + $(package)_config_opts=-DCMAKE_INSTALL_PREFIX=$($(package)_staging_dir)/$(host_prefix) + $(package)_config_opts+= -DCMAKE_PREFIX_PATH=$($(package)_staging_dir)/$(host_prefix) + $(package)_config_opts+= -DSTLIB=ON -DSHLIB=OFF -DSTBIN=ON + $(package)_config_opts_linux=-DOPSYS=LINUX -DCMAKE_SYSTEM_NAME=Linux + $(package)_config_opts_darwin=-DOPSYS=MACOSX -DCMAKE_SYSTEM_NAME=Darwin + $(package)_config_opts_mingw32=-DOPSYS=WINDOWS -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SHARED_LIBRARY_LINK_C_FLAGS="" -DCMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS="" + $(package)_config_opts_i686+= -DWSIZE=32 + $(package)_config_opts_x86_64+= -DWSIZE=64 + $(package)_config_opts_arm+= -DWSIZE=32 + $(package)_config_opts_debug=-DDEBUG=ON -DCMAKE_BUILD_TYPE=Debug + + ifneq ($(darwin_native_toolchain),) + $(package)_config_opts_darwin+= -DCMAKE_AR="$(host_prefix)/native/bin/$($(package)_ar)" + $(package)_config_opts_darwin+= -DCMAKE_RANLIB="$(host_prefix)/native/bin/$($(package)_ranlib)" + endif +endef + +define $(package)_config_cmds + export CC="$($(package)_cc)" && \ + export CXX="$($(package)_cxx)" && \ + export CFLAGS="$($(package)_cflags) $($(package)_cppflags)" && \ + export CXXFLAGS="$($(package)_cxxflags) $($(package)_cppflags)" && \ + export LDFLAGS="$($(package)_ldflags)" && \ + mkdir -p build && cd build && \ + cmake ../ $($(package)_config_opts) +endef + +define $(package)_build_cmds + cd build && \ + $(MAKE) $($(package)_build_opts) +endef + +define $(package)_stage_cmds + cd build && \ + $(MAKE) install +endef diff --git a/depends/packages/gmp.mk b/depends/packages/gmp.mk new file mode 100644 index 000000000000..0003b24a33da --- /dev/null +++ b/depends/packages/gmp.mk @@ -0,0 +1,22 @@ +package=gmp +$(package)_version=6.1.2 +$(package)_download_path=https://gmplib.org/download/gmp +$(package)_file_name=gmp-$($(package)_version).tar.bz2 +$(package)_sha256_hash=5275bb04f4863a13516b2f39392ac5e272f5e1bb8057b18aec1c9b79d73d8fb2 + +define $(package)_set_vars +$(package)_config_opts+=--enable-cxx --enable-fat --with-pic --disable-shared +endef + +define $(package)_config_cmds + $($(package)_autoconf) +endef + +define $(package)_build_cmds + $(MAKE) +endef + +define $(package)_stage_cmds + $(MAKE) DESTDIR=$($(package)_staging_dir) install +endef + diff --git a/depends/packages/native_biplist.mk b/depends/packages/native_biplist.mk index 3c6e8900f664..373490aedcaa 100644 --- a/depends/packages/native_biplist.mk +++ b/depends/packages/native_biplist.mk @@ -11,10 +11,10 @@ define $(package)_preprocess_cmds endef define $(package)_build_cmds - python setup.py build + python3 setup.py build endef define $(package)_stage_cmds mkdir -p $($(package)_install_libdir) && \ - python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) + python3 setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) endef diff --git a/depends/packages/native_ccache.mk b/depends/packages/native_ccache.mk index 4ed61a49e9ca..a297746795b4 100644 --- a/depends/packages/native_ccache.mk +++ b/depends/packages/native_ccache.mk @@ -1,9 +1,9 @@ package=native_ccache -$(package)_version=3.3.3 -$(package)_download_path=https://samba.org/ftp/ccache +$(package)_version=3.7.1 +$(package)_download_path=https://github.com/ccache/ccache/releases/download/v$($(package)_version) $(package)_file_name=ccache-$($(package)_version).tar.bz2 -$(package)_sha256_hash=2985bc5e32ebe38d2958d508eb54ddcad39eed909489c0c2988035214597ca54 - +$(package)_sha256_hash=1c6501d5bd01952f5535c3f11ca774aedf4711e373a7bea7e0b1f487f0789b19 +https://github.com/ccache/ccache/releases/download/v3.7.1/ccache-3.7.1.tar.bz2 define $(package)_set_vars $(package)_config_opts= endef diff --git a/depends/packages/native_ds_store.mk b/depends/packages/native_ds_store.mk index 49f5829ac1c0..e2d035d480c9 100644 --- a/depends/packages/native_ds_store.mk +++ b/depends/packages/native_ds_store.mk @@ -8,10 +8,10 @@ $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages $(package)_dependencies=native_biplist define $(package)_build_cmds - python setup.py build + python3 setup.py build endef define $(package)_stage_cmds mkdir -p $($(package)_install_libdir) && \ - python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) + python3 setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) endef diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk index 85a8a402bf92..9982a52a335c 100644 --- a/depends/packages/native_mac_alias.mk +++ b/depends/packages/native_mac_alias.mk @@ -12,10 +12,10 @@ define $(package)_preprocess_cmds endef define $(package)_build_cmds - python setup.py build + python3 setup.py build endef define $(package)_stage_cmds mkdir -p $($(package)_install_libdir) && \ - python setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) + python3 setup.py install --root=$($(package)_staging_dir) --prefix=$(build_prefix) --install-lib=$($(package)_install_libdir) endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 088723ebd0d5..9eabe53f3b14 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,4 +1,4 @@ -packages:=boost openssl libevent zeromq +packages:=boost openssl libevent zeromq gmp chia_bls native_packages := native_ccache qt_native_packages = native_protobuf diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 3006eebd595d..033d8362271c 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -1,6 +1,6 @@ PACKAGE=qt $(package)_version=5.7.1 -$(package)_download_path=http://download.qt.io/official_releases/qt/5.7/$($(package)_version)/submodules +$(package)_download_path=https://download.qt.io/archive/qt/5.7/$($(package)_version)/submodules $(package)_suffix=opensource-src-$($(package)_version).tar.gz $(package)_file_name=qtbase-$($(package)_suffix) $(package)_sha256_hash=95f83e532d23b3ddbde7973f380ecae1bac13230340557276f75f2e37984e410 @@ -9,7 +9,9 @@ $(package)_linux_dependencies=freetype fontconfig libxcb libX11 xproto libXext $(package)_build_subdir=qtbase $(package)_qt_libs=corelib network widgets gui plugins testlib $(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.patch -$(package)_patches+=fix_qt_pkgconfig.patch fix-cocoahelpers-macos.patch qfixed-coretext.patch +$(package)_patches+=fix_qt_configure.patch fix_qt_pkgconfig.patch fix-cocoahelpers-macos.patch qfixed-coretext.patch +# NOTE: fix_qt_configure.patch is only needed for Qt 5.7, newer versions don't have this issue. +# Remove it after bumping $(package)_version to 5.8+. $(package)_qttranslations_file_name=qttranslations-$($(package)_suffix) $(package)_qttranslations_sha256_hash=3a15aebd523c6d89fb97b2d3df866c94149653a26d27a00aac9b6d3020bc5a1d @@ -139,6 +141,7 @@ define $(package)_preprocess_cmds patch -p1 < $($(package)_patch_dir)/mingw-uuidof.patch && \ patch -p1 < $($(package)_patch_dir)/pidlist_absolute.patch && \ patch -p1 < $($(package)_patch_dir)/fix-xcb-include-order.patch && \ + patch -p1 < $($(package)_patch_dir)/fix_qt_configure.patch && \ patch -p1 < $($(package)_patch_dir)/fix_qt_pkgconfig.patch && \ patch -p1 < $($(package)_patch_dir)/fix-cocoahelpers-macos.patch && \ patch -p1 < $($(package)_patch_dir)/qfixed-coretext.patch && \ diff --git a/depends/packages/zlib.mk b/depends/packages/zlib.mk index 589490800f8b..e0600f0fe298 100644 --- a/depends/packages/zlib.mk +++ b/depends/packages/zlib.mk @@ -24,4 +24,3 @@ endef define $(package)_stage_cmds $(MAKE) DESTDIR=$($(package)_staging_dir) install $($(package)_build_opts) endef - diff --git a/depends/patches/qt/fix_qt_configure.patch b/depends/patches/qt/fix_qt_configure.patch new file mode 100644 index 000000000000..3466a6c24d79 --- /dev/null +++ b/depends/patches/qt/fix_qt_configure.patch @@ -0,0 +1,11 @@ +--- old/qtbase/configure ++++ new/qtbase/configure +@@ -2846,7 +2846,7 @@ + # with the system. We use 'xcrun' to check the clang version that's part of + # the Xcode installation. + XCRUN=`/usr/bin/xcrun -sdk macosx clang -v 2>&1` +- CLANGVERSION=`echo "$XCRUN" | sed -n 's/.*version \([0-9]\).*/\1/p'` ++ CLANGVERSION=`echo "$XCRUN" | sed -n 's/.*version \([0-9]*\).*/\1/p'` + expr "$CLANGVERSION" : '[0-9]' > /dev/null || { echo "Unable to determine CLANG version from output of xcrun: $XCRUN" ; exit 2 ; } + if [ "$CLANGVERSION" -ge 3 ]; then + PLATFORM=macx-clang diff --git a/dips/dip-0001.md b/dips/dip-0001.md deleted file mode 100644 index 55c2d73bc03a..000000000000 --- a/dips/dip-0001.md +++ /dev/null @@ -1,236 +0,0 @@ -
-  DIP: 0001
-  Title: Initial Scaling of the Network
-  Comments-Summary: No comments yet.
-  Status: Final
-  Type: Standard
-  Created: 2017-07-05
-  License: MIT License
-
- -## Table of Contents - -1. [Abstract](#abstract) -2. [Motivation](#motivation) -3. [Conventions](#conventions) -4. [Prior Work](#prior-work) -5. [Quadratic Hashing](#quadratic-hashing-challenge) -6. [Consesus Protocol Changes](#consensus-protocol-changes) -7. [Observation](#observation) -8. [BIP 009 Inspired Activation](#bip-009-inspired-activation) -9. [Calculation of thresholdreached](#calculation-of-thresholdreached) -10. [Comments on a Spork](#comments-on-a-spork) -11. [Copyright](#copyright) - -## Abstract - -We outline an initial scaling mechanism for Absolute. After deployment and activation, Absolute will be able to handle double the transactions it can currently handle. -This means that Absolute will be prepared for eight times the traffic of Bitcoin. - -## Motivation - -Peer to peer electronic cash systems rely on the fact that they can be used with little to no trust in a third party. -We have also seen scaling issues have dramatic effects on the usability of these systems. -The current state of the absolute network requires users to trust that developers will deliver a scaling solution when needed. -Implementation of DIP1 allows users to actively participate in increasing the capacity of Absolute. - -In addition, scaling solutions take time to roll out. -As such, developers are tasked with rolling out scaling solutions four or five months before they are needed. -Forecasting network traffic growth is an impossible task. -An exponential regression would estimate that the network will be at capacity around September of 2018. -However, there are outside factors that could accelerate this estimate. -For example, a chain fork which is being discussed for bitcoin may drive users to alternatives. -Also, market actors can have a profound impact on the use of Absolute, and do not necessarily announce their intentions for proprietary reasons. - -One more motivation is that adopting this proposal serves as a proof of concept of Absolutes’s governance system. -It shows that proposals voted on by masternodes do actually have real world results. -It removes the need for users to trust in Absolute’s proposal system. - -## Conventions - -* `0x20000000` is the bit sequence `00100000000000000000000000000000` and is the current version of Absolute blocks. -* `0x20000002` is the version used to signal acceptance of a consensus change rule. -A miner should use this version to signal acceptance of the miner and the masternode for the block of a consensus rule change. -* We use `%` as it is used in C++. It is the remainder on division. So `7 % 3` is 7 modulo 3 and is equal to 1. - -## Prior Work - -* [BIP 0009](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki) Improvement plan that signals a consensus rule change. -This allows for activation when a sufficient number of miners have upgraded. -* [BIP 0135](https://github.com/bitcoin/bips/blob/master/bip-0135.mediawiki) Alternate Improvement plan that signals a consensus rule change. -* [BIP 0113](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki) Specification of Median Time Past (MTP). - -## Quadratic Hashing Challenge - -Currently Absolute has a size limit of 1MB per block. With this limit a miner could include a transaction that takes around 3 minutes to verify on a moderate CPU. -However, with a 2MB block size the attacking miner could include a transaction that takes around 12 minutes to verify. If an attacker successfully gets an attacking block in the chain then all nodes in the future will need to verify this block when syncing. -This attack is even more dramatic as block size increases. -Paradoxically, this attack could get so bad that it becomes impossible because propagation would force an attacking block to be orphaned. -Even if an attacking block is not included in the main chain, trying to verify attacking blocks can have negative transient effects. - -This attack is quadratic with respect to *transaction* size. -As such, limiting transaction size makes this attack impossible. -Currently, transactions over a size of 100kB are nonstandard and dropped by the network. -A quadratic hashing attacking transaction of size 100kB would take around 2 seconds to verify. -Since transactions are processed in parallel, a 2MB block full of 100kB attacking transactions would take from 4 to 8 seconds to process. -For this reason this DIP changes consensus rules so that blocks with a transaction over 100kB are ruled invalid and orphaned. - -## Consensus Protocol Changes - -Currently there is a protocol rule as follows: -* A block over 1MB is invalid. - -To allow the network to scale we replace this rule with the two rules: -* A block over 2MB is invalid. -* A block with at least one transaction over 100kB is invalid. - -Note the 100kB cap on transaction size will not necessarily be satisfied by old blocks. -For this reason this new rule must only be enforced on blocks after activation of the protocol change. - -## Observation - -### 100kB Transaction Limit - -This subsection is to only point out a possible efficiency of the 100kB transaction size limit. -Currently, transactions in the mempool already verified to be standard and thus satisfy the size limit. -It should then be possible that only transactions not seen by the node would need to be checked to satisfy the 100kB limit. -However, checking if a transaction is in the mempool might take as long as verifying the size. - -## BIP 009 Inspired Activation - -To ensure a coherent network roll-out we employ BIP9 signaling adjusted for Absolute. -We reserve the last bit in the version to signal for DIP1 activation. -A DIP0001 enabled miner will broadcast block version `0x20000002` only if the masternode selected for that block is also DIP1 enabled. -We model the 2016 block difficulty adjustment period in BIP9 with a 4032 block round. -This length of a round should allow for a representative sample of masternodes to be taken. - -As a block is mined the version number is determined by the miner who mined it and the masternode that is selected for that block. -Note that blocks mined by non upgraded miners will broadcast version `0x20000000` without regard for the DIP1 acceptance of the masternode selected. - -As in BIP9 each block is assigned a state according to the flow chart: - -![State Flow Chart](/dip-0001/states.png) - -Above, MTP represents the median time past as in BIP113. The variables starttime and timeout are particular integers that represent the time in the current epoch. -Here, threshold reached, is a particular boolean we will define below, and denote without the space, `thresholdreached`, in code. - -The state of every block is determined by the algorithm below: - -The state of the Genesis block is DEFINED as in BIP009. - -```C++ -State GetStateForBlock(block) { - if (block.height == 0) { - return DEFINED; - } -``` - -All blocks in the same round have the same state. - -```C++ -if ((block.height % 4032) != 0) { - return GetStateForBlock(block.parent); - } -``` - -Otherwise, the next state depends on the previous state: - -```C++ -switch (GetStateForBlock(GetAncestorAtHeight(block, block.height - 4032))) { -``` - -We remain in the DEFINED state until we either pass the starttime or the timeout. -`GetMedianTimePast` in the code below refers to the median `nTime` of a block and its 10 predecessors. - -```C++ - case DEFINED: - if (GetMedianTimePast(block.parent) >= timeout) { - return FAILED; - } - if (GetMedianTimePast(block.parent) >= starttime) { - return STARTED; - } - return DEFINED; -``` - -After a period in the STARTED state, if we're past the timeout, we switch to FAILED. -If not, we tally the bits set, and transition to LOCKED_IN if a sufficient number of blocks in the past period have `bit1` set to 1. - -Note that a block's state never depends on its own nVersion; only on that of its ancestors. - -```C++ - case STARTED: - if (GetMedianTimePast(block.parent) >= timeout) { - return FAILED; - } - if (thresholdreached) { - return LOCKED_IN; - } - return STARTED; -``` - -The protocol change is only in force when the first block is in an ACTIVE state. - -## Selection of Parameters - -For values of starttime and timeout, we suggest: -* starttime= 1508025600-- turn of Oct 15 at midnight -* timeout= 1539561600-- 52 weeks later - -This is assuming the code is available mid September. -If the code is available at a later release these numbers should be adjusted. - -The selection of a round consisting of 4032 blocks is made based on masternodes not being randomly selected, rather they are in a queue. -As such it can’t treat the sample of masternodes as a simple random sample. -At current time 4032 masternodes would consist of a sample of around 87% of masternodes. -This number is small enough that the sample of masternodes are unique. -If this number were larger then there would be a chance that some masternodes would be sampled twice while others aren’t sampled at all. -This property is maintained as long as the number of blocks in a round is under 90% of all masternodes. -The number 4032 also has the property of being equal to about a week of blocks. - -## Calculation of thresholdreached - -### Determination of thresholdreached - -Given a block with block.height % 4032 = 0 we can calculate `thresholdreached`. `thresholdreached` is determined by `bit1` on each version of all blocks in the previous round. -Note the current block is not considered to be in the previous round. -We define `thresholdreached` to be true when and only when, - -`(sum(bit 1) >= 3226)`. - -This sum is over `(block.height - 4032)` through `(block.height - 1)`. - -This translates to 80% of the DIP1 flags. - -### Expected activation conditions. - -Given that bit1 will be 0 if mined by a non-DIP1 miner this actually targets activation in the following cases. - -* `>=80%` of mining power and `100%` of masternodes - -OR -* `>= 89.5%` of mining power and `>= 89.5%` of masternodes - -OR -* `100%` of mining power and `>= 80%` of masternodes - -In any case, activation is highly unlikely without at least 80% of miners *and* 80% of masternodes. - -## Comments on a Spork - -It has been suggested that a spork variable be used to possibly delay activation if there is some type of hiccup with roll out. -In my estimation, using a spork variable could introduce variables that might lead to unintended consequences. - -This rollout requires all nodes to agree on an activation block height. -DIP1 as outlined above only relies on an inspection of the chain to determine the activation block height. -The blockchain was created to solve just this problem of distributed nodes agreeing on information. -At this time I do not see how this level of security could be achieved with something not on chain. - -It is also important that the protocol never roll back to the previous state. -If this ever happened then new nodes would not be able to sync with the main chain. -Also existing nodes may not agree on a chain if the spork was ever rolled back. - - -## Copyright - -Copyright (c) 2018 Absolute Core Team. [Licensed under MIT License](https://opensource.org/licenses/MIT) \ No newline at end of file diff --git a/dips/dip-0002.md b/dips/dip-0002.md deleted file mode 100644 index a08ef38249f2..000000000000 --- a/dips/dip-0002.md +++ /dev/null @@ -1,46 +0,0 @@ -
-  DIP: 0002
-  Title: Changing Block Timing and annual reward reduction
-  Comments-Summary: No comments yet.
-  Status: Final
-  Type: Standard
-  Created: 2017-07-05
-  License: MIT License
-
- -## Table of Contents - -1. [Abstract](#abstract) -2. [Motivation](#motivation) -3. [Conventions](#conventions) -4. [Observation](#observation) -5. [Copyright](#copyright) - -## Abstract - - - -## Motivation - - - -## Conventions - - -## Quadratic Hashing Challenge - - - -## Consensus Protocol Changes - - - -## Observation - -## BIP 002 Inspired Activation - - - -## Copyright - -Copyright (c) 2018 Absolute Core Team. [Licensed under MIT License](https://opensource.org/licenses/MIT) \ No newline at end of file diff --git a/doc/Doxyfile b/doc/Doxyfile index bd67dc70276d..31c949897bce 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -41,7 +41,7 @@ PROJECT_NAME = "Absolute Core" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.12.2.5 +PROJECT_NUMBER = 0.12.3.1 # 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 @@ -2353,8 +2353,8 @@ DIRECTORY_GRAPH = YES # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). # Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, -# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and -# png:gdiplus:gdiplus. +# png:cairo:gd, png:cairo:cairo, png:cairo:gaiplus, png:gaiplus and +# png:gaiplus:gaiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. diff --git a/doc/README.md b/doc/README.md index b5f62ca15b3c..0d51fc4102e9 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,4 +1,4 @@ -Absolute 0.12.2 +Absolute 0.12.3.1 ===================== You may find below instructions for setting up Absolute wallet. diff --git a/doc/README_windows.txt b/doc/README_windows.txt index 1a90bb4d4953..5cde71443a10 100644 --- a/doc/README_windows.txt +++ b/doc/README_windows.txt @@ -1,4 +1,4 @@ -Absolute Core 0.12.2.5 +Absolute Core 0.12.3.1 ===================== Intro diff --git a/doc/build-cross.md b/doc/build-cross.md new file mode 100644 index 000000000000..da1936498d34 --- /dev/null +++ b/doc/build-cross.md @@ -0,0 +1,95 @@ +Cross-compiliation of Absolute Core +=============================== + +Absolute Core can be cross-compiled on Linux to all other supported host systems. This is done by changing +the `HOST` parameter when building the dependencies and then specifying another `--prefix` directory when building Absolute. + +The following instructions are only tested on Debian Stretch and Ubuntu Bionic. + +MacOSX Cross-compilation +------------------------ +Cross-compiling to MacOSX requires a few additional packages to be installed: + +```bash +$ sudo apt-get install python3-setuptools libcap-dev zlib1g-dev libbz2-dev +``` + +Additionally, the Mac OSX SDK must be downloaded and extracted manually: + +```bash +$ mkdir -p depends/sdk-sources +$ mkdir -p depends/SDKs +$ curl https://bitcoincore.org/depends-sources/sdks/MacOSX10.11.sdk.tar.gz -o depends/sdk-sources/MacOSX10.11.sdk.tar.gz +$ tar -C depends/SDKs -xf depends/sdk-sources/MacOSX10.11.sdk.tar.gz +``` + +When building the dependencies, as described in [build-generic](build-generic.md), use + +```bash +$ make HOST=x86_64-apple-darwin11 -j4 +``` + +When building Absolute Core, use + +```bash +$ ./configure --prefix `pwd`/depends/x86_64-apple-darwin11 +``` + +Windows 64bit/32bit Cross-compilation +------------------------------- +Cross-compiling to Windows requires a few additional packages to be installed: + +```bash +$ sudo apt-get install nsis wine-stable wine64 bc +``` + +For Windows 64bit, install : +```bash +$ sudo apt-get install g++-mingw-w64-x86-64 +$ # Required to enable C++ threading libraries (e.g. std::thread) +$ sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix +$ sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix +``` + +For Windows 32bit, install: +```bash +$ sudo apt-get install g++-mingw-w64-i686 +$ # Required to enable C++ threading libraries (e.g. std::thread) +$ sudo update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix +$ sudo update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix +``` + +When building the dependencies, as described in [build-generic](build-generic.md), use + +```bash +$ make HOST=x86_64-w64-mingw32 -j4 +``` + +When building Absolute Core, use + +```bash +$ ./configure --prefix `pwd`/depends/x86_64-w64-mingw32 +``` + +These commands will build for Windows 64bit. If you want to compile for 32bit, +replace `x86_64-w64-mingw32` with `i686-w64-mingw32`. + +ARM-Linux Cross-compilation +------------------- +Cross-compiling to ARM-Linux requires a few additional packages to be installed: + +```bash +$ sudo apt-get install g++-arm-linux-gnueabihf +``` + +When building the dependencies, as described in [build-generic](build-generic.md), use + +```bash +$ make HOST=arm-linux-gnueabihf -j4 +``` + +When building Absolute Core, use + +```bash +$ ./configure --prefix `pwd`/depends/arm-linux-gnueabihf +``` diff --git a/doc/build-generic.md b/doc/build-generic.md new file mode 100644 index 000000000000..97da67dcc74e --- /dev/null +++ b/doc/build-generic.md @@ -0,0 +1,90 @@ +GENERIC BUILD NOTES +==================== +Some notes on how to build Absolute Core based on the [depends](../depends/README.md) build system. + +Note on old build instructions +------------------------------ +In the past, the build documentation contained instructions on how to build Absolute with system-wide installed dependencies +like BerkeleyDB 4.8, boost and Qt. Building this way is considered deprecated and only building with the `depends` prefix +is supported today. + +Required build tools and environment +------------------------------------ +Building the dependencies and Absolute Core requires some essential build tools to be installed before. Please see +[build-unix](build-unix.md), [build-osx](build-osx.md) and [build-windows](build-windows.md) for details. + +Building dependencies +--------------------- +Absolute inherited the `depends` folder from Bitcoin, which contains all dependencies required to build Absolute. These +dependencies must be built before Absolute can actually be built. To do so, perform the following: + +```bash +$ cd depends +$ make -j4 # Choose a good -j value, depending on the number of CPU cores available +$ cd .. +``` + +This will download and build all dependencies required to build Absolute Core. Caching of build results will ensure that only +the packages are rebuilt which have changed since the last depends build. + +It is required to re-run the above commands from time to time when dependencies have been updated or added. If this is +not done, build failures might occur when building Absolute. + +Please read the [depends](../depends/README.md) documentation for more details on supported hosts and configuration +options. If no host is specified (as in the above example) when calling `make`, the depends system will default to your +local host system. + +Building Absolute Core +--------------------- + +```bash +$ ./autogen.sh + +Pick one of the following configure setups: +$ ./configure --prefix `pwd`/depends/i686-pc-linux-gnu +$ ./configure --prefix `pwd`/depends/x86_64-pc-linux-gnu +$ ./configure --prefix `pwd`/depends/i686-w64-mingw32 +$ ./configure --prefix `pwd`/depends/x86_64-w64-mingw32 +$ ./configure --prefix `pwd`/depends/x86_64-apple-darwin11 +$ ./configure --prefix `pwd`/depends/arm-linux-gnueabihf +$ ./configure --prefix `pwd`/depends/aarch64-linux-gnu + +$ make +$ make install # optional +``` + +The following triplets are usually valid, use the right one for your host compiler: +- `i686-pc-linux-gnu` for Linux32 +- `x86_64-pc-linux-gnu` for Linux64 +- `i686-w64-mingw32` for Win32 +- `x86_64-w64-mingw32` for Win64 +- `x86_64-apple-darwin11` for MacOSX +- `arm-linux-gnueabihf` for Linux ARM 32 bit +- `aarch64-linux-gnu` for Linux ARM 64 bit + +If you want to cross-compile for another platform, choose the appropriate `` and make sure to build the +dependencies with the same host before. + +If you want to build for the same host but different distro, add `--enable-glibc-back-compat` when calling `./configure`. + + +ccache +------ +The depends system also contains [ccache](https://ccache.samba.org/), which caches build results on source->object +level. `./configure` of Absolute Core will autodetect the presence of ccache and enable use of it. To disable ccache, use +`./configure --prefix= --disable-ccache`. + +The default maximum cache size is 5G, which might not be enough to cache multiple builds when switching Git branches +very often. It is advised to increase the maximum cache size: + +```bash +$ ./depends//native/bin/ccache -M20G +``` + +Additional Configure Flags +-------------------------- +A list of additional configure flags can be displayed with: + +```bash +./configure --help +``` diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md deleted file mode 100644 index 209f038cbb7b..000000000000 --- a/doc/build-openbsd.md +++ /dev/null @@ -1,170 +0,0 @@ -OpenBSD build guide -====================== -(updated for OpenBSD 6.0) - -This guide describes how to build absoluted and command-line utilities on OpenBSD. - -As OpenBSD is most common as a server OS, we will not bother with the GUI. - -Preparation -------------- - -Run the following as root to install the base dependencies for building: - -```bash -pkg_add gmake libtool libevent -pkg_add autoconf # (select highest version, e.g. 2.69) -pkg_add automake # (select highest version, e.g. 1.15) -pkg_add python # (select highest version, e.g. 3.5) -``` - -The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Absolute Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler. -GCC -------- - -You can install a newer version of gcc with: - -```bash -pkg_add g++ # (select newest 4.x version, e.g. 4.9.3) -``` - -This compiler will not overwrite the system compiler, it will be installed as `egcc` and `eg++` in `/usr/local/bin`. - -### Building boost - -Do not use `pkg_add boost`! The boost version installed thus is compiled using the `g++` compiler not `eg++`, which will result in a conflict between `/usr/local/lib/libestdc++.so.XX.0` and `/usr/lib/libstdc++.so.XX.0`, resulting in a test crash: - - test_absolute:/usr/lib/libstdc++.so.57.0: /usr/local/lib/libestdc++.so.17.0 : WARNING: symbol(_ZN11__gnu_debug17_S_debug_me ssagesE) size mismatch, relink your program - ... - Segmentation fault (core dumped) - -This makes it necessary to build boost, or at least the parts used by Absolute Core, manually: - -``` -# Pick some path to install boost to, here we create a directory within the absolute directory -BITCOIN_ROOT=$(pwd) -BOOST_PREFIX="${BITCOIN_ROOT}/boost" -mkdir -p $BOOST_PREFIX - -# Fetch the source and verify that it is not tampered with -curl -o boost_1_61_0.tar.bz2 http://heanet.dl.sourceforge.net/project/boost/boost/1.61.0/boost_1_61_0.tar.bz2 -echo 'a547bd06c2fd9a71ba1d169d9cf0339da7ebf4753849a8f7d6fdb8feee99b640 boost_1_61_0.tar.bz2' | sha256 -c -# MUST output: (SHA256) boost_1_61_0.tar.bz2: OK -tar -xjf boost_1_61_0.tar.bz2 - -# Boost 1.61 needs one small patch for OpenBSD -cd boost_1_61_0 -# Also here: https://gist.githubusercontent.com/laanwj/bf359281dc319b8ff2e1/raw/92250de8404b97bb99d72ab898f4a8cb35ae1ea3/patch-boost_test_impl_execution_monitor_ipp.patch -patch -p0 < /usr/ports/devel/boost/patches/patch-boost_test_impl_execution_monitor_ipp - -# Build w/ minimum configuration necessary for absolute -echo 'using gcc : : eg++ : "-fvisibility=hidden -fPIC" "" "ar" "strip" "ranlib" "" : ;' > user-config.jam -config_opts="runtime-link=shared threadapi=pthread threading=multi link=static variant=release --layout=tagged --build-type=complete --user-config=user-config.jam -sNO_BZIP2=1" -./bootstrap.sh --without-icu --with-libraries=chrono,filesystem,program_options,system,thread,test -./b2 -d2 -j2 -d1 ${config_opts} --prefix=${BOOST_PREFIX} stage -./b2 -d0 -j4 ${config_opts} --prefix=${BOOST_PREFIX} install -``` - -### Building BerkeleyDB - -BerkeleyDB is only necessary for the wallet functionality. To skip this, pass `--disable-wallet` to `./configure`. - -See "Berkeley DB" in [build_unix.md](build_unix.md) for instructions on how to build BerkeleyDB 4.8. -You cannot use the BerkeleyDB library from ports, for the same reason as boost above (g++/libstd++ incompatibility). - -```bash -# Pick some path to install BDB to, here we create a directory within the absolute directory -BITCOIN_ROOT=$(pwd) -BDB_PREFIX="${BITCOIN_ROOT}/db4" -mkdir -p $BDB_PREFIX - -# Fetch the source and verify that it is not tampered with -curl -o db-4.8.30.NC.tar.gz 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' -echo '12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef db-4.8.30.NC.tar.gz' | sha256 -c -# MUST output: (SHA256) db-4.8.30.NC.tar.gz: OK -tar -xzf db-4.8.30.NC.tar.gz - -# Build the library and install to specified prefix -cd db-4.8.30.NC/build_unix/ -# Note: Do a static build so that it can be embedded into the executable, instead of having to find a .so at runtime -../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX CC=egcc CXX=eg++ CPP=ecpp -make install # do NOT use -jX, this is broken -``` - -### Resource limits -The standard ulimit restrictions in OpenBSD are very strict: - data(kbytes) 1572864 -This is, unfortunately, no longer enough to compile some `.cpp` files in the project, -at least with gcc 4.9.3 (see issue https://github.com/bitcoin/bitcoin/issues/6658). -If your user is in the `staff` group the limit can be raised with: - ulimit -d 3000000 -The change will only affect the current shell and processes spawned by it. To -make the change system-wide, change `datasize-cur` and `datasize-max` in -`/etc/login.conf`, and reboot. - -### Building Absolute Core - -**Important**: use `gmake`, not `make`. The non-GNU `make` will exit with a horrible error. - -Preparation: -```bash -export AUTOCONF_VERSION=2.69 # replace this with the autoconf version that you installed -export AUTOMAKE_VERSION=1.15 # replace this with the automake version that you installed -./autogen.sh -``` -Make sure `BDB_PREFIX` and `BOOST_PREFIX` are set to the appropriate paths from the above steps. - -To configure with wallet: -```bash -./configure --with-gui=no --with-boost=$BOOST_PREFIX \ - CC=egcc CXX=eg++ CPP=ecpp \ - BDB_LIBS="-L${BDB_PREFIX}/lib -ldb_cxx-4.8" BDB_CFLAGS="-I${BDB_PREFIX}/include" -``` - -To configure without wallet: -```bash -./configure --disable-wallet --with-gui=no --with-boost=$BOOST_PREFIX \ - CC=egcc CXX=eg++ CPP=ecpp -``` - -Build and run the tests: -```bash -gmake # can use -jX here for parallelism -gmake check -``` - -Clang (not currently working) ------------------------------- - -WARNING: This is outdated, needs to be updated for OpenBSD 6.0 and re-tried. - -Using a newer g++ results in linking the new code to a new libstdc++. -Libraries built with the old g++, will still import the old library. -This gives conflicts, necessitating rebuild of all C++ dependencies of the application. - -With clang this can - at least theoretically - be avoided because it uses the -base system's libstdc++. - -```bash -pkg_add llvm boost -``` - -```bash -./configure --disable-wallet --with-gui=no CC=clang CXX=clang++ -gmake -``` - -However, this does not appear to work. Compilation succeeds, but link fails -with many 'local symbol discarded' errors: - - local symbol 150: discarded in section `.text._ZN10tinyformat6detail14FormatIterator6finishEv' from libbitcoin_util.a(libbitcoin_util_a-random.o) - local symbol 151: discarded in section `.text._ZN10tinyformat6detail14FormatIterator21streamStateFromFormatERSoRjPKcii' from libbitcoin_util.a(libbitcoin_util_a-random.o) - local symbol 152: discarded in section `.text._ZN10tinyformat6detail12convertToIntIA13_cLb0EE6invokeERA13_Kc' from libbitcoin_util.a(libbitcoin_util_a-random.o) - -According to similar reported errors this is a binutils (ld) issue in 2.15, the -version installed by OpenBSD 5.7: - -- http://openbsd-archive.7691.n7.nabble.com/UPDATE-cppcheck-1-65-td248900.html -- https://llvm.org/bugs/show_bug.cgi?id=9758 - -There is no known workaround for this. diff --git a/doc/build-osx.md b/doc/build-osx.md index be20a971bf51..2a8e3fbde589 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -13,42 +13,22 @@ When the popup appears, click `Install`. Then install [Homebrew](https://brew.sh). -Dependencies ----------------------- +Base build dependencies +----------------------- - brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf qt libevent +```bash +brew install automake libtool --c++11 pkg-config +``` If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG +```bash +brew install librsvg +``` - brew install librsvg +Building +-------- -NOTE: Building with Qt4 is still supported, however, doing so could result in a broken UI. Therefore, building with Qt5 is recommended. - -Build Absolute Core ------------------------- - -1. Clone the Absolute Core source code and cd into `absolute` - - git clone https://github.com/absolute-community/absolute - cd absolute - -2. Build Absolute Core: - - Configure and build the headless absolute binaries as well as the GUI (if Qt is found). - - You can disable the GUI build by passing `--without-gui` to configure. - - ./autogen.sh - ./configure - make - -3. It is recommended to build and run the unit tests: - - make check - -4. You can also create a .dmg that contains the .app bundle (optional): - - make deploy +Follow the instructions in [build-generic](build-generic.md) Running ------- @@ -90,10 +70,3 @@ Uncheck everything except Qt Creator during the installation process. 8. Select the default "Desktop" kit and select "Clang (x86 64bit in /usr/bin)" as compiler 9. Select LLDB as debugger (you might need to set the path to your installation) 10. Start debugging with Qt Creator - -Notes ------ - -* Tested on OS X 10.8 through 10.12 on 64-bit Intel processors only. - -* Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714) diff --git a/doc/build-unix.md b/doc/build-unix.md index 4c80aaf0a6c8..857cabdf8ce4 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -4,202 +4,39 @@ Some notes on how to build Absolute Core in Unix. (for OpenBSD specific instructions, see [build-openbsd.md](build-openbsd.md)) -Note ---------------------- -Always use absolute paths to configure and compile Absolute Core and the dependencies, -for example, when specifying the path of the dependency: +Base build dependencies +----------------------- +Building the dependencies and Absolute Core requires some essential build tools and libraries to be installed before. - ../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX - -Here BDB_PREFIX must be an absolute path - it is defined using $(pwd) which ensures -the usage of the absolute path. - -To Build ---------------------- +Run the following commands to install required packages: +##### Debian/Ubuntu: ```bash -./autogen.sh -./configure -make -make install # optional +$ sudo apt-get install curl build-essential libtool autotools-dev automake pkg-config python3 bsdmainutils cmake ``` -This will build absolute-qt as well if the dependencies are met. - -Dependencies ---------------------- - -These dependencies are required: - - Library | Purpose | Description - ------------|------------------|---------------------- - libssl | Crypto | Random Number Generation, Elliptic Curve Cryptography - libboost | Utility | Library for threading, data structures, etc - libevent | Networking | OS independent asynchronous networking - -Optional dependencies: - - Library | Purpose | Description - ------------|------------------|---------------------- - miniupnpc | UPnP Support | Firewall-jumping support - libdb4.8 | Berkeley DB | Wallet storage (only needed when wallet enabled) - qt | GUI | GUI toolkit (only needed when GUI enabled) - protobuf | Payments in GUI | Data interchange format used for payment protocol (only needed when GUI enabled) - libqrencode | QR codes in GUI | Optional for generating QR codes (only needed when GUI enabled) - univalue | Utility | JSON parsing and encoding (bundled version will be used unless --with-system-univalue passed to configure) - libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.x) - -For the versions used in the release, see [release-process.md](release-process.md) under *Fetch and build inputs*. - -Memory Requirements --------------------- - -C++ compilers are memory-hungry. It is recommended to have at least 1.5 GB of -memory available when compiling Absolute Core. On systems with less, gcc can be -tuned to conserve memory with additional CXXFLAGS: - - ./configure CXXFLAGS="--param ggc-min-expand=1 --param ggc-min-heapsize=32768" - -Dependency Build Instructions: Ubuntu & Debian ----------------------------------------------- -Build requirements: - - sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils - -Options when installing required Boost library files: -1. On at least Ubuntu 14.04+ and Debian 7+ there are generic names for the -individual boost development packages, so the following can be used to only -install necessary parts of boost: - - sudo apt-get install libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev - -2. If that doesn't work, you can install all boost development packages with: - - sudo apt-get install libboost-all-dev - -BerkeleyDB is required for the wallet. - -**For Ubuntu only:** db4.8 packages are available [here](https://launchpad.net/~bitcoin/+archive/bitcoin). -You can add the repository and install using the following commands: - - sudo apt-get install software-properties-common - sudo add-apt-repository ppa:bitcoin/bitcoin - sudo apt-get update - sudo apt-get install libdb4.8-dev libdb4.8++-dev - -Ubuntu and Debian have their own libdb-dev and libdb++-dev packages, but these will install -BerkeleyDB 5.1 or later, which break binary wallet compatibility with the distributed executables which -are based on BerkeleyDB 4.8. If you do not care about wallet compatibility, -pass `--with-incompatible-bdb` to configure. - -See the section "Disable-wallet mode" to build Absolute Core without wallet. - -Optional (see --with-miniupnpc and --enable-upnp-default): - - sudo apt-get install libminiupnpc-dev - -ZMQ dependencies (provides ZMQ API 4.x): - - sudo apt-get install libzmq3-dev - -Dependencies for the GUI: Ubuntu & Debian ------------------------------------------ - -If you want to build Absolute-Qt, make sure that the required packages for Qt development -are installed. Either Qt 5 or Qt 4 are necessary to build the GUI. -If both Qt 4 and Qt 5 are installed, Qt 5 will be used. Pass `--with-gui=qt4` to configure to choose Qt4. -To build without GUI pass `--without-gui`. - -To build with Qt 5 (recommended) you need the following: - - sudo apt-get install libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libprotobuf-dev protobuf-compiler - -Alternatively, to build with Qt 4 you need the following: - - sudo apt-get install libqt4-dev libprotobuf-dev protobuf-compiler - -libqrencode (optional) can be installed with: - - sudo apt-get install libqrencode-dev - -Once these are installed, they will be found by configure and a absolute-qt executable will be -built by default. - -Dependency Build Instructions: Fedora -------------------------------------- -Build requirements: - - sudo dnf install gcc-c++ libtool make autoconf automake openssl-devel libevent-devel boost-devel libdb4-devel libdb4-cxx-devel - -Optional: - - sudo dnf install miniupnpc-devel - -To build with Qt 5 (recommended) you need the following: - - sudo dnf install qt5-qttools-devel qt5-qtbase-devel protobuf-devel - -libqrencode (optional) can be installed with: - - sudo dnf install qrencode-devel - -Notes ------ -The release is built with GCC and then "strip absoluted" to strip the debug -symbols, which reduces the executable size by about 90%. - - -miniupnpc ---------- - -[miniupnpc](http://miniupnp.free.fr/) may be used for UPnP port mapping. It can be downloaded from [here]( -http://miniupnp.tuxfamily.org/files/). UPnP support is compiled in and -turned off by default. See the configure options for upnp behavior desired: - - --without-miniupnpc No UPnP support miniupnp not required - --disable-upnp-default (the default) UPnP support turned off by default at runtime - --enable-upnp-default UPnP support turned on by default at runtime - - -Berkeley DB ------------ -It is recommended to use Berkeley DB 4.8. If you have to build it yourself: - +##### Fedora: ```bash -ABSOLUTE_ROOT=$(pwd) - -# Pick some path to install BDB to, here we create a directory within the absolute directory -BDB_PREFIX="${ABSOLUTE_ROOT}/db4" -mkdir -p $BDB_PREFIX - -# Fetch the source and verify that it is not tampered with -wget 'http://download.oracle.com/berkeley-db/db-4.8.30.NC.tar.gz' -echo '12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef db-4.8.30.NC.tar.gz' | sha256sum -c -# -> db-4.8.30.NC.tar.gz: OK -tar -xzvf db-4.8.30.NC.tar.gz - -# Build the library and install to our prefix -cd db-4.8.30.NC/build_unix/ -# Note: Do a static build so that it can be embedded into the executable, instead of having to find a .so at runtime -../dist/configure --enable-cxx --disable-shared --with-pic --prefix=$BDB_PREFIX -make install - -# Configure Absolute Core to use our own-built instance of BDB -cd $ABSOLUTE_ROOT -./autogen.sh -./configure LDFLAGS="-L${BDB_PREFIX}/lib/" CPPFLAGS="-I${BDB_PREFIX}/include/" # (other args...) +$ sudo dnf install gcc-c++ libtool make autoconf automake python3 cmake libstdc++-static patch ``` -**Note**: You only need Berkeley DB if the wallet is enabled (see the section *Disable-Wallet mode* below). +##### Arch Linux: +```bash +$ pacman -S base-devel python3 cmake +``` -Boost ------ -If you need to build Boost yourself: +##### FreeBSD/OpenBSD: +```bash +pkg_add gmake cmake libtool +pkg_add autoconf # (select highest version, e.g. 2.69) +pkg_add automake # (select highest version, e.g. 1.15) +pkg_add python # (select highest version, e.g. 3.5) +``` - sudo su - ./bootstrap.sh - ./bjam install +Building +-------- +Follow the instructions in [build-generic](build-generic.md) Security -------- @@ -209,8 +46,8 @@ This can be disabled with: Hardening Flags: - ./configure --enable-hardening - ./configure --disable-hardening + ./configure --prefix= --enable-hardening + ./configure --prefix= --disable-hardening Hardening enables the following features: @@ -255,7 +92,7 @@ Disable-wallet mode When the intention is to run only a P2P node without a wallet, Absolute Core may be compiled in disable-wallet mode with: - ./configure --disable-wallet + ./configure --prefix= --disable-wallet In this case there is no dependency on Berkeley DB 4.8. @@ -268,76 +105,40 @@ A list of additional configure flags can be displayed with: ./configure --help -Setup and Build Example: Arch Linux ------------------------------------ -This example lists the steps necessary to setup and build a command line only, non-wallet distribution of the latest changes on Arch Linux: - - pacman -S git base-devel boost libevent python - git clone https://github.com/absolute-community/absolute.git - cd absolute/ - ./autogen.sh - ./configure --disable-wallet --without-gui --without-miniupnpc - make check - -Note: -Enabling wallet support requires either compiling against a Berkeley DB newer than 4.8 (package `db`) using `--with-incompatible-bdb`, -or building and depending on a local version of Berkeley DB 4.8. The readily available Arch Linux packages are currently built using -`--with-incompatible-bdb` according to the [PKGBUILD](https://projects.archlinux.org/svntogit/community.git/tree/bitcoin/trunk/PKGBUILD). -As mentioned above, when maintaining portability of the wallet between the standard Absolute Core distributions and independently built -node software is desired, Berkeley DB 4.8 must be used. - -ARM Cross-compilation -------------------- -These steps can be performed on, for example, an Ubuntu VM. The depends system -will also work on other Linux distributions, however the commands for -installing the toolchain will be different. - -Make sure you install the build requirements mentioned above. -Then, install the toolchain and curl: - - sudo apt-get install g++-arm-linux-gnueabihf curl - -To build executables for ARM: - - cd depends - apt-get install curl - make HOST=arm-linux-gnueabihf NO_QT=1 - cd .. - ./configure --prefix=$PWD/depends/arm-linux-gnueabihf --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++ - make - - -For further documentation on the depends system see [README.md](../depends/README.md) in the depends directory. Building on FreeBSD -------------------- -(Updated as of FreeBSD 11.0) - -Clang is installed by default as `cc` compiler, this makes it easier to get -started than on [OpenBSD](build-openbsd.md). Installing dependencies: +(TODO, this is untested, please report if it works and if changes to this documentation are needed) - pkg install autoconf automake libtool pkgconf - pkg install boost-libs openssl libevent - pkg install gmake +Building on FreeBSD is basically the same as on Linux based systems, with the difference that you have to use `gmake` +instead of `make`. -You need to use GNU make (`gmake`) instead of `make`. -(`libressl` instead of `openssl` will also work) +*Note on debugging*: The version of `gdb` installed by default is [ancient and considered harmful](https://wiki.freebsd.org/GdbRetirement). +It is not suitable for debugging a multi-threaded C++ program, not even for getting backtraces. Please install the package `gdb` and +use the versioned gdb command e.g. `gdb7111`. -For the wallet (optional): +Building on OpenBSD +------------------- - pkg install db5 +(TODO, this is untested, please report if it works and if changes to this documentation are needed) +(TODO, clang might also be an option. Old documentation reported it to to not work due to linking errors, but we're building all dependencies now as part of the depends system, so this might have changed) -This will give a warning "configure: WARNING: Found Berkeley DB other -than 4.8; wallets opened by this build will not be portable!", but as FreeBSD never -had a binary release, this may not matter. If backwards compatibility -with 4.8-built Absolute Core is needed follow the steps under "Berkeley DB" above. +Building on OpenBSD might require installation of a newer GCC version. If needed, do this with: -Then build using: +```bash +$ pkg_add g++ # (select newest 6.x version) +``` - ./autogen.sh - ./configure --with-incompatible-bdb BDB_CFLAGS="-I/usr/local/include/db5" BDB_LIBS="-L/usr/local/lib -ldb_cxx-5" - gmake +This compiler will not overwrite the system compiler, it will be installed as `egcc` and `eg++` in `/usr/local/bin`. -*Note on debugging*: The version of `gdb` installed by default is [ancient and considered harmful](https://wiki.freebsd.org/GdbRetirement). -It is not suitable for debugging a multi-threaded C++ program, not even for getting backtraces. Please install the package `gdb` and -use the versioned gdb command e.g. `gdb7111`. +Add `CC=egcc CXX=eg++ CPP=ecpp` to the dependencies build and the Dash Core build: +```bash +$ cd depends +$ make CC=egcc CXX=eg++ CPP=ecpp # do not use -jX, this is broken +$ cd .. +$ export AUTOCONF_VERSION=2.69 # replace this with the autoconf version that you installed +$ export AUTOMAKE_VERSION=1.15 # replace this with the automake version that you installed +$ ./autogen.sh +$ ./configure --prefix= CC=egcc CXX=eg++ CPP=ecpp +$ gmake # do not use -jX, this is broken +``` diff --git a/doc/build-windows.md b/doc/build-windows.md index b4d5e499db62..e0bb0757e974 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -1,7 +1,7 @@ WINDOWS BUILD NOTES ==================== -Some notes on how to build Absolute Core for Windows. +Below are some notes on how to build Absolute Core for Windows. Most developers use cross-compilation from Ubuntu to build executables for Windows. This is also used to build the release binaries. @@ -46,51 +46,8 @@ recommended but it is possible to compile the 32-bit version. Cross-compilation ------------------- -These steps can be performed on, for example, an Ubuntu VM. The depends system -will also work on other Linux distributions, however the commands for -installing the toolchain will be different. -First, install the general dependencies: - - sudo apt-get install build-essential libtool autotools-dev automake pkg-config bsdmainutils curl - -A host toolchain (`build-essential`) is necessary because some dependency -packages (such as `protobuf`) need to build host utilities that are used in the -build process. - -## Building for 64-bit Windows - -To build executables for Windows 64-bit, install the following dependencies: - - sudo apt-get install g++-mingw-w64-x86-64 mingw-w64-x86-64-dev - -Then build using: - - cd depends - make HOST=x86_64-w64-mingw32 - cd .. - ./autogen.sh # not required when building from tarball - CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site ./configure --prefix=/ - make - -## Building for 32-bit Windows - -To build executables for Windows 32-bit, install the following dependencies: - - sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev - -Then build using: - - cd depends - make HOST=i686-w64-mingw32 - cd .. - ./autogen.sh # not required when building from tarball - CONFIG_SITE=$PWD/depends/i686-w64-mingw32/share/config.site ./configure --prefix=/ - make - -## Depends system - -For further documentation on the depends system see [README.md](../depends/README.md) in the depends directory. +Follow the instructions for Windows in [build-cross](build-cross.md) Installation ------------- diff --git a/doc/developer-notes.md b/doc/developer-notes.md index e7664a8c3764..d77f4d4813bf 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -188,8 +188,6 @@ Threads - BitcoinMiner : Generates coins (if wallet is enabled). -- ThreadCheckDarkSendPool : Runs masternode list and sync data update loops - - Shutdown : Does an orderly shutdown of everything. Ignoring IDE/editor files diff --git a/doc/gitian-building.md b/doc/gitian-building.md index c5b847a880f8..1986f5f52c71 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -268,14 +268,14 @@ echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc # make /etc/rc.local script that sets up bridge between guest and host echo '#!/bin/sh -e' > /etc/rc.local echo 'brctl addbr lxcbr0' >> /etc/rc.local -echo 'ifconfig lxcbr0 10.0.3.2/24 up' >> /etc/rc.local +echo 'ifconfig lxcbr0 10.0.3.1/24 up' >> /etc/rc.local echo 'iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE' >> /etc/rc.local echo 'echo 1 > /proc/sys/net/ipv4/ip_forward' >> /etc/rc.local echo 'exit 0' >> /etc/rc.local # make sure that USE_LXC is always set when logging in as debian, # and configure LXC IP addresses echo 'export USE_LXC=1' >> /home/debian/.profile -echo 'export GITIAN_HOST_IP=10.0.3.2' >> /home/debian/.profile +echo 'export GITIAN_HOST_IP=10.0.3.1' >> /home/debian/.profile echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/debian/.profile reboot ``` @@ -307,15 +307,15 @@ Clone the git repositories for Absolute Core and Gitian. ```bash git clone https://github.com/devrandom/gitian-builder.git -git clone https://github.com/absolute-community/absolute -git clone https://github.com/absolute-community//gitian.signatures.git +git clone https://github.com/absolute-community/absolute.git +git clone https://github.com/absolute-community/gitian.signatures.git ``` Setting up the Gitian image ------------------------- Gitian needs a virtual image of the operating system to build in. -Currently this is Ubuntu Trusty x86_64. +Currently this is Ubuntu Bionic x86_64. This image will be copied and used every time that a build is started to make sure that the build is deterministic. Creating the image will take a while, but only has to be done once. @@ -324,7 +324,7 @@ Execute the following as user `debian`: ```bash cd gitian-builder -bin/make-base-vm --lxc --arch amd64 --suite trusty +bin/make-base-vm --lxc --arch amd64 --suite bionic ``` There will be a lot of warnings printed during the build of the image. These can be ignored. @@ -342,7 +342,8 @@ There will be a lot of warnings printed during the build of the image. These can Getting and building the inputs -------------------------------- -At this point you have two options, you can either use the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)) or you could manually do everything by following this guide. If you're using the automated script, then run it with the "--setup" command. Afterwards, run it with the "--build" command (example: "contrib/gitian-building.sh -b signer 0.13.0"). Otherwise ignore this. +At this point you have two options, you can either use the automated script (found in [contrib/gitian-build.py](/contrib/gitian-build.py)) or you could manually do everything by following this guide. If you're using the automated script, then run it with the "--setup" command. Afterwards, run it with the "--build" command (example: "contrib/gitian-building.sh -b signer 0.13.0"). Otherwise ignore this. + Follow the instructions in [doc/release-process.md](release-process.md#fetch-and-create-inputs-first-time-or-when-dependency-versions-change) in the Absolute Core repository under 'Fetch and create inputs' to install sources which require manual intervention. Also optionally follow the next step: 'Seed the Gitian sources cache @@ -375,7 +376,7 @@ Output from `gbuild` will look something like Resolving deltas: 100% (41590/41590), done. From https://github.com/absolute-community/absolute ... (new tags, new branch etc) - --- Building for trusty amd64 --- + --- Building for bionic amd64 --- Stopping target if it is up Making a new image copy stdin: is not a tty @@ -424,14 +425,14 @@ So, if you use LXC: export PATH="$PATH":/path/to/gitian-builder/libexec export USE_LXC=1 cd /path/to/gitian-builder -./libexec/make-clean-vm --suite trusty --arch amd64 +./libexec/make-clean-vm --suite bionic --arch amd64 -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get update -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root \ +LXC_ARCH=amd64 LXC_SUITE=bionic on-target -u root apt-get update +LXC_ARCH=amd64 LXC_SUITE=bionic on-target -u root \ -e DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -y install \ $( sed -ne '/^packages:/,/[^-] .*/ {/^- .*/{s/"//g;s/- //;p}}' ../absolute/contrib/gitian-descriptors/*|sort|uniq ) -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get -q -y purge grub -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root -e DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade +LXC_ARCH=amd64 LXC_SUITE=bionic on-target -u root apt-get -q -y purge grub +LXC_ARCH=amd64 LXC_SUITE=bionic on-target -u root -e DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade ``` And then set offline mode for apt-cacher-ng: diff --git a/doc/instantsend.md b/doc/instantsend.md index 70b5df824fdd..18f1e6c8adb0 100644 --- a/doc/instantsend.md +++ b/doc/instantsend.md @@ -25,56 +25,4 @@ When a wallet InstantSend transaction is successfully locked a shell command pro #### RPC -Details pertaining to an observed "Transaction Lock" can also be retrieved through RPC, it’s important however to understand the underlying mechanism. - -By default, the Absolute Core daemon will launch using the following constant: - -``` -static const int DEFAULT_INSTANTSEND_DEPTH = 5; -``` - -This value can be overridden by passing the following argument to the Absolute Core daemon: - -``` --instantsenddepth= -``` - -The key thing to understand is that this value indicates the number of "confirmations" a successful Transaction Lock represents. When Wallet RPC commands which support `minconf` and `addlockconf` parameters (such as `listreceivedbyaddress`) are performed and `addlockconf` is `true`, then `instantsenddepth` attribute is taken into account when returning information about the transaction. In this case the value in `confirmations` field you see through RPC is showing the number of `"Blockchain Confirmations" + "InstantSend Depth"` (assuming the funds were sent via InstantSend). - -There is also a field named `instantlock` (that is present in commands such as `listsinceblock`). The value in this field indicates whether a given transaction is locked via InstantSend. - -**Examples** - -1. `listreceivedbyaddress 0 true` - * InstantSend transaction just occurred: - * confirmations: 5 - * InstantSend transaction received one confirmation from blockchain: - * confirmations: 6 - * non-InstantSend transaction just occurred: - * confirmations: 0 - * non-InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - -2. `listreceivedbyaddress 0` - * InstantSend transaction just occurred: - * confirmations: 0 - * InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - * non-InstantSend transaction just occurred: - * confirmations: 0 - * non-InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - -3. `listsinceblock` - * InstantSend transaction just occurred: - * confirmations: 0 - * instantlock: true - * InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - * instantlock: true - * non-InstantSend transaction just occurred: - * confirmations: 0 - * instantlock: false - * non-InstantSend transaction received one confirmation from blockchain: - * confirmations: 1 - * instantlock: false +Details pertaining to an observed "Transaction Lock" can also be retrieved through RPC. There is a boolean field named `instantlock` which indicates whether a given transaction is locked via InstantSend. This field is present in the output of some wallet RPC commands e.g. `listsinceblock`, `gettransaction` etc. as well as in the output of some mempool RPC commands e.g. `getmempoolentry` and a couple of others like `getrawmempool` (for `verbose=true` only). diff --git a/doc/man/absolute-cli.1 b/doc/man/absolute-cli.1 index a55222b8ea89..057cd13e3146 100644 --- a/doc/man/absolute-cli.1 +++ b/doc/man/absolute-cli.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ABSOLUTE-CLI "1" "January 2018" "absolute-cli v0.12.2.5" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH ABSOLUTE-CLI "1" "June 2020" "absolute-cli v0.12.3.1" "User Commands" .SH NAME -absolute-cli \- manual page for absolute-cli v0.12.3.0 +absolute-cli \- manual page for absolute-cli v0.12.3.1 .SH DESCRIPTION -Absolute Core RPC client version v0.12.3.0 +Absolute Core RPC client version v0.12.3.1 .SS "Usage:" .TP absolute\-cli [options] [params] @@ -75,8 +75,9 @@ Timeout during HTTP requests (default: 900) Read extra arguments from standard input, one per line until EOF/Ctrl\-D (recommended for sensitive information such as passphrases) .SH COPYRIGHT -Copyright (C) 2014-2017 The Absolute Core developers -Copyright (C) 2009-2017 The Bitcoin Core developers +Copyright (C) 2014-2020 The Absolute Core developers +Copyright (C) 2014-2020 The Dash Core developers +Copyright (C) 2009-2020 The Bitcoin Core developers Please contribute if you find Absolute Core useful. Visit for further information about the software. diff --git a/doc/man/absolute-qt.1 b/doc/man/absolute-qt.1 index b775ace83754..f9c9b3f8a59e 100644 --- a/doc/man/absolute-qt.1 +++ b/doc/man/absolute-qt.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ABSOLUTE-QT "1" "January 2018" "absolute-qt v0.12.2.5" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH ABSOLUTE-QT "1" "June 2020" "absolute-qt v0.12.3.1" "User Commands" .SH NAME -absolute-qt \- manual page for absolute-qt v0.12.3.0 +absolute-qt \- manual page for absolute-qt v0.12.3.1 .SH DESCRIPTION -Absolute Core version v0.12.3.0 (64\-bit) +Absolute Core version v0.12.3.1 (64\-bit) Usage: .IP absolute\-qt [command\-line options] @@ -36,9 +36,9 @@ block hash) If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: -000000000000001c172f518793c3b9e83f202284615592f87fe3506ce964dcd4, +0000000000000024e26c7df7e46d673724d223cf4ca2b2adc21297cc095600f4, testnet: -0000000004f5aef732d572ff514af99a995702c92e4452c7af10858231668b1f) +000000000118fa2f031efedf874adbe7fe6b023cdf9478e8e2400c2d505df30d) .HP \fB\-conf=\fR .IP @@ -76,7 +76,7 @@ Extra transactions to keep in memory for compact block reconstructions .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-4\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-2\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP \fB\-pid=\fR @@ -419,6 +419,19 @@ Enable publish hash transaction in
.IP Enable publish hash transaction (locked via InstantSend) in
.HP +\fB\-zmqpubhashgovernancevote=\fR
+.IP +Enable publish hash of governance votes in
+.HP +\fB\-zmqpubhashgovernanceobject=\fR
+.IP +Enable publish hash of governance objects (like proposals) in
+.HP +\fB\-zmqpubhashinstantsenddoublespend=\fR
+.IP +Enable publish transaction hashes of attempted InstantSend double spend +in
+.HP \fB\-zmqpubrawblock=\fR
.IP Enable publish raw block in
@@ -430,6 +443,11 @@ Enable publish raw transaction in
\fB\-zmqpubrawtxlock=\fR
.IP Enable publish raw transaction (locked via InstantSend) in
+.HP +\fB\-zmqpubrawinstantsenddoublespend=\fR
+.IP +Enable publish raw transactions of attempted InstantSend double spend in +
.PP Debugging/Testing options: .HP @@ -469,7 +487,7 @@ relaying, mining and transaction creation (default: 0.00001) .IP Maximum total fees (in ABS) to use in a single wallet transaction or raw transaction; setting this too low may abort large -transactions (default: 0.20) +transactions (default: 0.10) .HP \fB\-printtoconsole\fR .IP @@ -502,6 +520,12 @@ InstantSend, Governance) (0\-1, default: 0) .IP Override spork address. Only useful for regtest and povnet. Using this on mainnet or testnet will ban you. +.HP +\fB\-minsporkkeys=\fR +.IP +Overrides minimum spork signers to change spork value. Only useful for +regtest and povnet. Using this on mainnet or testnet will ban +you. .PP Masternode options: .HP @@ -533,10 +557,14 @@ Enable use of automated PrivateSend for funds stored in this wallet Enable multiple PrivateSend mixing sessions per block, experimental (0\-1, default: 0) .HP +\fB\-privatesendsessions=\fR +.IP +Use N separate masternodes in parallel to mix funds (1\-10, default: 4) +.HP \fB\-privatesendrounds=\fR .IP Use N separate masternodes for each denominated input to mix funds -(2\-16, default: 2) +(2\-16, default: 4) .HP \fB\-privatesendamount=\fR .IP @@ -555,11 +583,6 @@ InstantSend options: Enable InstantSend, show confirmations for locked transactions (0\-1, default: 1) .HP -\fB\-instantsenddepth=\fR -.IP -Show N confirmations for a successfully locked transaction (0\-60, -default: 5) -.HP \fB\-instantsendnotify=\fR .IP Execute command when a wallet InstantSend transaction is successfully @@ -579,10 +602,6 @@ Relay and mine data carrier transactions (default: 1) .IP Maximum size of data in data carrier transactions we relay and mine (default: 83) -.HP -\fB\-mempoolreplacement\fR -.IP -Enable transaction replacement in the memory pool (default: 0) .PP Block creation options: .HP @@ -679,8 +698,9 @@ Show splash screen on startup (default: 1) .IP Reset all settings changed in the GUI .SH COPYRIGHT -Copyright (C) 2014-2017 The Absolute Core developers -Copyright (C) 2009-2017 The Bitcoin Core developers +Copyright (C) 2014-2020 The Absolute Core developers +Copyright (C) 2014-2020 The Dash Core developers +Copyright (C) 2009-2020 The Bitcoin Core developers Please contribute if you find Absolute Core useful. Visit for further information about the software. diff --git a/doc/man/absolute-tx.1 b/doc/man/absolute-tx.1 index b821d076ee27..205b60389602 100644 --- a/doc/man/absolute-tx.1 +++ b/doc/man/absolute-tx.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ABSOLUTE-TX "1" "January 2018" "absolute-tx v0.12.3.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH ABSOLUTE-TX "1" "June 2020" "absolute-tx v0.12.3.1" "User Commands" .SH NAME -absolute-tx \- manual page for absolute-tx v0.12.3.0 +Absolute-tx \- manual page for absolute-tx v0.12.3.1 .SH DESCRIPTION -Absolute Core absolute\-tx utility version v0.12.3.0 +Absolute Core absolute\-tx utility version v0.12.3.1 .SS "Usage:" .TP absolute\-tx [options] [commands] @@ -108,8 +108,9 @@ set=NAME:JSON\-STRING .IP Set register NAME to given JSON\-STRING .SH COPYRIGHT -Copyright (C) 2014-2017 The Absolute Core developers -Copyright (C) 2009-2017 The Bitcoin Core developers +Copyright (C) 2014-2020 The Absolute Core developers +Copyright (C) 2014-2020 The Dash Core developers +Copyright (C) 2009-2020 The Bitcoin Core developers Please contribute if you find Absolute Core useful. Visit for further information about the software. diff --git a/doc/man/absoluted.1 b/doc/man/absoluted.1 index dc7e5e205e15..73aaf238b193 100644 --- a/doc/man/absoluted.1 +++ b/doc/man/absoluted.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH ABSOLUTED "1" "January 2018" "absoluted v0.12.2.5" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH ABSOLUTED "1" "June 2020" "absoluted v0.12.3.1" "User Commands" .SH NAME -absoluted \- manual page for absoluted v0.12.3.0 +absoluted \- manual page for absoluted v0.12.3.1 .SH DESCRIPTION -Absolute Core Daemon version v0.12.3.0 +Absolute Core Daemon version v0.12.3.1 .SS "Usage:" .TP absoluted [options] @@ -81,7 +81,7 @@ Extra transactions to keep in memory for compact block reconstructions .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-4\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-2\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP \fB\-pid=\fR @@ -420,6 +420,19 @@ Enable publish hash transaction in
.IP Enable publish hash transaction (locked via InstantSend) in
.HP +\fB\-zmqpubhashgovernancevote=\fR
+.IP +Enable publish hash of governance votes in
+.HP +\fB\-zmqpubhashgovernanceobject=\fR
+.IP +Enable publish hash of governance objects (like proposals) in
+.HP +\fB\-zmqpubhashinstantsenddoublespend=\fR
+.IP +Enable publish transaction hashes of attempted InstantSend double spend +in
+.HP \fB\-zmqpubrawblock=\fR
.IP Enable publish raw block in
@@ -431,6 +444,11 @@ Enable publish raw transaction in
\fB\-zmqpubrawtxlock=\fR
.IP Enable publish raw transaction (locked via InstantSend) in
+.HP +\fB\-zmqpubrawinstantsenddoublespend=\fR
+.IP +Enable publish raw transactions of attempted InstantSend double spend in +
.PP Debugging/Testing options: .HP @@ -470,7 +488,7 @@ relaying, mining and transaction creation (default: 0.00001) .IP Maximum total fees (in ABS) to use in a single wallet transaction or raw transaction; setting this too low may abort large -transactions (default: 0.20) +transactions (default: 0.10) .HP \fB\-printtoconsole\fR .IP @@ -490,9 +508,9 @@ Chain selection options: .IP Use the test chain .HP -\fB\-devnet=\fR +\fB\-povnet=\fR .IP -Use devnet chain with provided name +Use povnet chain with provided name .HP \fB\-litemode=\fR .IP @@ -503,6 +521,12 @@ InstantSend, Governance) (0\-1, default: 0) .IP Override spork address. Only useful for regtest and povnet. Using this on mainnet or testnet will ban you. +.HP +\fB\-minsporkkeys=\fR +.IP +Overrides minimum spork signers to change spork value. Only useful for +regtest and povnet. Using this on mainnet or testnet will ban +you. .PP Masternode options: .HP @@ -534,14 +558,18 @@ Enable use of automated PrivateSend for funds stored in this wallet Enable multiple PrivateSend mixing sessions per block, experimental (0\-1, default: 0) .HP +\fB\-privatesendsessions=\fR +.IP +Use N separate masternodes in parallel to mix funds (1\-10, default: 4) +.HP \fB\-privatesendrounds=\fR .IP Use N separate masternodes for each denominated input to mix funds -(2\-16, default: 2) +(2\-16, default: 4) .HP \fB\-privatesendamount=\fR .IP -Keep N DASH anonymized (2\-21000000, default: 1000) +Keep N ABS anonymized (2\-21000000, default: 1000) .HP \fB\-liquidityprovider=\fR .IP @@ -556,11 +584,6 @@ InstantSend options: Enable InstantSend, show confirmations for locked transactions (0\-1, default: 1) .HP -\fB\-instantsenddepth=\fR -.IP -Show N confirmations for a successfully locked transaction (0\-60, -default: 5) -.HP \fB\-instantsendnotify=\fR .IP Execute command when a wallet InstantSend transaction is successfully @@ -580,10 +603,6 @@ Relay and mine data carrier transactions (default: 1) .IP Maximum size of data in data carrier transactions we relay and mine (default: 83) -.HP -\fB\-mempoolreplacement\fR -.IP -Enable transaction replacement in the memory pool (default: 0) .PP Block creation options: .HP @@ -654,8 +673,9 @@ option can be specified multiple times .IP Set the number of threads to service RPC calls (default: 4) .SH COPYRIGHT -Copyright (C) 2014-2017 The Absolute Core developers -Copyright (C) 2009-2017 The Bitcoin Core developers +Copyright (C) 2017-2020 The Absolute Core developers +Copyright (C) 2014-2020 The Dash Core developers +Copyright (C) 2009-2020 The Bitcoin Core developers Please contribute if you find Absolute Core useful. Visit for further information about the software. diff --git a/doc/masternode/install.sh b/doc/masternode/install.sh index b78cbe001f04..fb77f320b094 100644 --- a/doc/masternode/install.sh +++ b/doc/masternode/install.sh @@ -33,4 +33,4 @@ sudo apt-get install -y pwgen EXIP=`wget -qO- ipinfo.io/ip` PASS=`pwgen -1 20 -n` printf "rpcuser=user\nrpcpassword=$PASS\nrpcport=18889\ndaemon=1\nlisten=1\nserver=1\nmaxconnections=256\nrpcallowip=127.0.0.1\nexternalip=$EXIP:18888\n" > /$HOME/.absolutecore/absolute.conf -printf "\n#--- new nodes ---\naddnode=139.99.41.241:18888\naddnode=139.99.41.242:18888\naddnode=139.99.202.1:18888\n" >> /$HOME/.absolutecore/absolute.conf +printf "\n#--- new nodes ---\naddnode=88.198.95.174:18888\naddnode=135.181.33.18:18888\naddnode=45.77.138.219:18888\naddnode=95.216.209.25:18888\naddnode=116.203.202.68:18888\n" >> /$HOME/.absolutecore/absolute.conf diff --git a/doc/masternode/install_multi_mn_ubuntu_1604.sh b/doc/masternode/install_multi_mn_ubuntu.sh similarity index 90% rename from doc/masternode/install_multi_mn_ubuntu_1604.sh rename to doc/masternode/install_multi_mn_ubuntu.sh index 330335715cd7..aedfadfb340b 100644 --- a/doc/masternode/install_multi_mn_ubuntu_1604.sh +++ b/doc/masternode/install_multi_mn_ubuntu.sh @@ -11,8 +11,8 @@ systemd_unit_path="/etc/systemd/system" abs_unit_file="absmn" # when new wallet release is published the next two lines needs to be updated -wallet_ver="v12.2.5" -wallet_file="absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz" +wallet_ver="v12.3.1" +wallet_file="absolutecore-0.12.3-x86_64-linux-gnu" wallet_url="https://github.com/absolute-community/absolute/releases/download/$wallet_ver" @@ -52,8 +52,7 @@ function setupNode { printf "\n#--- basic configuration --- \nrpcuser=$abs_user\nrpcpassword=$rpc_pass\nrpcport=$rpc_port\nbind=$mn_ip:$abs_port\nrpcbind=127.0.0.1:$rpc_port\nexternalip=$mn_ip:$abs_port\ndaemon=1\nlisten=1\nserver=1\nmaxconnections=256\nrpcallowip=127.0.0.1\n" printf "\n#--- masternode ---\nmasternode=1\nmasternodeprivkey=$privkey\n" - printf "\n#--- new nodes ---\naddnode=45.77.138.219:18888\naddnode=192.3.134.140:18888\naddnode=107.174.102.130:18888\naddnode=107.173.70.103:18888\naddnode=107.173.70.105:18888\naddnode=107.174.142.252:18888\naddnode=54.93.66.231:18888\naddnode=66.23.197.121:18888\n" - printf "addnode=45.63.99.215:18888\naddnode=45.77.134.248:18888\naddnode=140.82.46.194:18888\naddnode=139.99.96.203:18888\naddnode=139.99.40.157:18888\naddnode=139.99.41.35:18888\naddnode=139.99.41.198:18888\naddnode=139.99.44.0:18888\n" + printf "\n#--- new nodes ---\naddnode=88.198.95.174:18888\naddnode=135.181.33.18:18888\naddnode=45.77.138.219:18888\naddnode=95.216.209.25:18888\naddnode=116.203.202.68:18888\n" } > "$abs_conf_file" } @@ -113,19 +112,20 @@ printf "\n%s" "Installed OS: $(cut -d':' -f2 <<< "$(lsb_release -d)")" printf "\n%s\n" "We are now in $(pwd) directory" printf "ABS nodes will be installed in curent path!\n\n" -# check ubuntu version - we need 16.04 -if [ -r /etc/os-release ]; then - . /etc/os-release - if [ "${VERSION_ID}" != "16.04" ] ; then - echo "Script needs Ubuntu 16.04, exiting!" - echo - exit 1 - fi -else - echo "Operating system is not Ubuntu 16.04, exiting!" - echo - exit 1 -fi +# check ubuntu version - we need 16.04 +if [ -r /etc/os-release ]; then + . /etc/os-release + if [ "${VERSION_ID}" != "ubuntu" ] ; then + echo "Script needs Ubuntu 16.04, exiting!" + echo + exit 1 + fi +else + echo "Operating system is not Ubuntu 16.04, exiting!" + echo + exit 1 +fi + # check syntax if [ $# == 0 ]; then diff --git a/doc/masternode/install_multi_mn_ubuntu_1804.sh b/doc/masternode/install_multi_mn_ubuntu_1804.sh deleted file mode 100644 index e1e73cbe644d..000000000000 --- a/doc/masternode/install_multi_mn_ubuntu_1804.sh +++ /dev/null @@ -1,291 +0,0 @@ -#!/bin/bash - -# set script vars -abs_user="ABS_mn_user" -abs_port=18888 -rpc_port=59001 -root_path="$(pwd)" -abscore_root_path="$root_path/.absolutecore" -wallet_path="$root_path/Absolute" -systemd_unit_path="/etc/systemd/system" -abs_unit_file="absmn" - -# when new wallet release is published the next two lines needs to be updated -wallet_ver="v12.2.5" -wallet_file="absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz" - -wallet_url="https://github.com/absolute-community/absolute/releases/download/$wallet_ver" - -function printError { - printf "\33[0;31m%s\033[0m\n" "$1" -} - -function printSuccess { - printf "\33[0;32m%s\033[0m\n" "$1" -} - -function printWarning { - printf "\33[0;33m%s\033[0m\n" "$1" -} - -function extractDaemon -{ - echo "Extracting..." - tar -zxvf "$wallet_file" && mv "$wallet_dir_name/bin" "$wallet_path" - rm -r "$wallet_dir_name" - if [ -f "/usr/local/bin/absolute-cli" ]; then - rm /usr/local/bin/absolute-cli - fi - if [ -f "/usr/local/bin/absoluted" ]; then - rm /usr/local/bin/absoluted - fi - ln -s "$wallet_path"/absolute-cli /usr/local/bin/absolute-cli - ln -s "$wallet_path"/absoluted /usr/local/bin/absoluted - rm "$wallet_file" - printSuccess "...done!" -} - -function setupNode -{ - mkdir -p "$abscore_path" && touch "$abs_conf_file" - - { - printf "\n#--- basic configuration --- \nrpcuser=$abs_user\nrpcpassword=$rpc_pass\nrpcport=$rpc_port\nbind=$mn_ip:$abs_port\nrpcbind=127.0.0.1:$rpc_port\nexternalip=$mn_ip:$abs_port\ndaemon=1\nlisten=1\nserver=1\nmaxconnections=256\nrpcallowip=127.0.0.1\n" - printf "\n#--- masternode ---\nmasternode=1\nmasternodeprivkey=$privkey\n" - printf "\n#--- new nodes ---\naddnode=45.77.138.219:18888\naddnode=192.3.134.140:18888\naddnode=107.174.102.130:18888\naddnode=107.173.70.103:18888\naddnode=107.173.70.105:18888\naddnode=107.174.142.252:18888\naddnode=54.93.66.231:18888\naddnode=66.23.197.121:18888\n" - printf "addnode=45.63.99.215:18888\naddnode=45.77.134.248:18888\naddnode=140.82.46.194:18888\naddnode=139.99.96.203:18888\naddnode=139.99.40.157:18888\naddnode=139.99.41.35:18888\naddnode=139.99.41.198:18888\naddnode=139.99.44.0:18888\n" - } > "$abs_conf_file" -} - -function setupSentinel -{ - echo "*** Installing sentinel for masternode $((count+1)) ***" - cd "$abscore_path" || return - git clone https://github.com/absolute-community/sentinel.git --q - cd "$sentinel_path" && virtualenv ./venv && ./venv/bin/pip install -r requirements.txt - printSuccess "...done!" - - echo - echo "*** Configuring sentinel ***" - if grep -q -x "absolute_conf=$abs_conf_file" "$sentinel_conf_file" ; then - printWarning "absolute.conf path already set in sentinel.conf!" - else - printf "absolute_conf=%s\n" "$abs_conf_file" >> "$sentinel_conf_file" - printSuccess "...done!" - fi -} - -function setupCrontab -{ - echo - echo "*** Configuring crontab ***" - - echo - echo "Set sentinel to run at every minute..." - if crontab -l 2>/dev/null | grep -q -x "\* \* \* \* \* cd $sentinel_path && ./venv/bin/python bin/sentinel.py >/dev/null 2>&1" >/dev/null ; then - printWarning "Sentinel run at every minute already set!" - else - (crontab -l 2>/dev/null; echo "* * * * * cd $sentinel_path && ./venv/bin/python bin/sentinel.py >/dev/null 2>&1") | crontab - - printSuccess "...done!" - fi -} - - -function setupABSd -{ - touch "$absd" - { - printf "Description=Start ABS daemon\n\nWants=network.target\nAfter=syslog.target network-online.target\n" - printf "\n[Service]\nType=forking\nTimeoutSec=15\nExecStart=$wallet_path/absoluted -datadir=$abscore_path -daemon\n" - printf "ExecStop=$wallet_path/absolute-cli -datadir=$abscore_path stop\n" - printf "ExecReload=/bin/kill -SIGHUP \$MAINPID\n" - printf "Restart=on-failure\nRestartSec=15\nKillMode=process\n" - printf "\n[Install]\nWantedBy=multi-user.target\n" - } > "$absd" -} - - -# entry point -clear - -printf "\n%s\n" "===== ABS multinode vps install =====" -printf "\n%s" "Installed OS: $(cut -d':' -f2 <<< "$(lsb_release -d)")" -printf "\n%s\n" "We are now in $(pwd) directory" -printf "ABS nodes will be installed in curent path!\n\n" - -# check ubuntu version - we need 18.04 -if [ -r /etc/os-release ]; then - . /etc/os-release - if [ "${VERSION_ID}" != "18.04" ] ; then - echo "Script needs Ubuntu 18.04, exiting!" - echo - exit 1 - fi -else - echo "Operating system is not Ubuntu 18.04, exiting!" - echo - exit 1 -fi - -# check syntax -if [ $# == 0 ]; then - printError "Masternodes privkeys missing!!!" - printWarning "Usage $0 mn_privkey1 mn_privkey2 mn_privkey3 ..." - printWarning "This script will configure a number of nodes as many as the number of privatekeys provided as arguments." - printWarning "The number of masternode privatekeys must not be greater then the number of ips your vps has." - printWarning "Make sure you have at least 512Mb of RAM for each instance on your vps!" - echo - exit 0 -fi - -# get the number of ips -ips_no=$(ip -4 addr show | grep 'inet ' | awk '{print $2}' | cut -f1 -d'/' | grep -v '^127\.\|^10\.\|^172\.1[6-9]\.\|^172\.2[0-9]\.\|^172\.3[0-2]\.\|^192\.168\.' -c) -printSuccess "We found $ips_no ip address(es)!" - -if [ $# -gt "$ips_no" ]; then - printWarning "Number of privkeys is greater then number of ip addresses." - printWarning "Check number of privkeys provided and try again." - echo - exit 0 -fi - -# let's do this -echo -printSuccess "You provided $# privkey(s)!" -echo -printSuccess "We will install $# masternode(s)!" -echo - -echo "*** Updating system ***" -apt-get update -y -qq -apt-get upgrade -y -qq -printSuccess "...done!" - -echo -echo "*** Install ABS daemon dependencies ***" -apt-get install nano mc dbus ufw fail2ban htop git pwgen python virtualenv python-virtualenv software-properties-common -y -qq -add-apt-repository ppa:bitcoin/bitcoin -y -apt-get update -y -qq -apt-get upgrade -y -qq -apt-get install libdb4.8-dev libdb4.8++-dev -y -qq -printSuccess "...done!" - -echo -echo "*** Download ABS daemon binaries ***" -if [ ! -f "$wallet_file" ]; then - echo "Downloading..." - wget "$wallet_url/$wallet_file" -q && printSuccess "...done!" -else - printWarning "File already downloaded!" -fi - -wallet_dir_name=$(tar -tzf $wallet_file | head -1 | cut -f1 -d"/") - -if [ -z "$wallet_dir_name" ]; then - printError "Failed - downloading ABS daemon binaries." - exit 1 -fi - -echo -echo "*** Extract ABS daemon binaries ***" -if [ -d "$wallet_path" ]; then - printWarning "Daemon directory already exist!" - printWarning "Checking for running ABS daemon!" - if [ -z "$(pgrep "absoluted")" ]; then - printWarning "Running daemon not found!" - else - printError "Running daemon found! Killing it..." - kill -9 "$(pgrep "absoluted")" - sleep 10 - printSuccess "...done!" - fi - printWarning "Remove old daemon directory..." - rm -r "$wallet_path" - printSuccess "...done!" - echo -fi -extractDaemon - -# get the ip list -echo -echo "*** Get available ips ***" -declare -a ip_list -mapfile -t ip_list < <(ip -4 addr show | grep 'inet ' | awk '{print $2}' | cut -f1 -d'/' | grep -v '^127\.\|^10\.\|^172\.1[6-9]\.\|^172\.2[0-9]\.\|^172\.3[0-2]\.\|^192\.168\.') -echo "${ip_list[*]}" -printSuccess "...done!" - -# find open rpc ports -echo -echo "*** Find necessary open rpc ports ***" -count=0 -declare -a rpc_ports -while [ "$count" -lt "$#" ]; do - if [ 0 -eq "$(netstat -tunlep | grep $rpc_port -c)" ]; then - echo "Found RPC port $rpc_port open" - rpc_ports["$count"]="$rpc_port" - ((++count)) - fi - ((++rpc_port)) -done -printSuccess "...done!" - -#configure folders, conf files, sentinel and crontab -echo -echo "*** Creating masternode folders and configuration ***" -count=0 -for privkey in "${@}"; do - rpc_pass=$(pwgen -1 20 -n) - rpc_port="${rpc_ports[$count]}" - mn_ip="${ip_list[$count]}" - - abscore_path="$abscore_root_path$((count+1))" - abs_conf_file="$abscore_path/absolute.conf" - - sentinel_path="$abscore_path/sentinel" - sentinel_conf_file="$sentinel_path/sentinel.conf" - - printSuccess "Configure ABS masternode $((count+1)) in $abscore_path with following settings:" - printSuccess " - ip: $mn_ip:$abs_port" - printSuccess " - private key: $privkey" - - echo - if [ ! -d "$abscore_path" ]; then - - setupNode - - setupSentinel - - setupCrontab - - absd="$systemd_unit_path/$abs_unit_file$((count+1)).service" - setupABSd - systemctl enable "$abs_unit_file$((count+1))" - else - printError "Configuration directory found! Remove $abscore_path directory or configure daemon manually!" - printError "Failed - masternode configuration." - echo - exit 1 - fi - echo - cd "$root_path" - printSuccess "Masternode $((count+1)) configuration ...done!" - ((++count)) - echo -done - -echo "*** Following nodes were set up ***" -count=1 -for privkey in "${@}"; do - mn_ip="${ip_list[$count-1]}" - printSuccess "Node $count was set up on ip $mn_ip:$abs_port with private key $privkey" - ((++count)) -done - -echo -echo "That's it! Everything is done! You just have to start the masternode(s) with next command(s):" -count=0 -while [ "$count" -lt "$#" ]; do - printSuccess "systemctl start $abs_unit_file$((count+1))" - ((++count)) -done -echo diff --git a/doc/masternode/install_ubuntu_1604.sh b/doc/masternode/install_ubuntu.sh similarity index 87% rename from doc/masternode/install_ubuntu_1604.sh rename to doc/masternode/install_ubuntu.sh index 840ac7b1d5f7..9641f8e3a994 100644 --- a/doc/masternode/install_ubuntu_1604.sh +++ b/doc/masternode/install_ubuntu.sh @@ -71,18 +71,18 @@ echo "Step 1 : Updating packages" { echo "Step 2 : Downloading binaries - extract" - if [ ! -f absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz ]; then + if [ ! -f absolutecore-0.12.3-x86_64-linux-gnu.tar.gz ]; then echo "Dowloading..." - wget https://github.com/absolute-community/absolute/releases/download/v12.2.5/absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz -O absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz -q + wget https://github.com/absolute-community/absolute/releases/download/v12.3.1/absolutecore-0.12.3-x86_64-linux-gnu.tar.gz -O absolutecore-0.12.3-x86_64-linux-gnu.tar.gz -q else printWarning "File already exist" fi if [ ! -d Absolute ]; then echo "Extracting" - tar -zxvf absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz && + tar -zxvf absolutecore-0.12.3-x86_64-linux-gnu.tar.gz && echo "Rename daemon folder" - mv absolutecore-0.12.2.5/bin Absolute + mv absolutecore-0.12.3/bin Absolute sudo ln -s $root_path/Absolute/absolute-cli /usr/local/bin/absolute-cli sudo ln -s $root_path/Absolute/absoluted /usr/local/bin/absoluted else @@ -90,7 +90,7 @@ echo "Step 1 : Updating packages" fi printSuccess "Done" -} || { +} || {absolutecore-0.12.3-x86_64-linux-gnu printError "Fail" exit 1 } @@ -108,7 +108,7 @@ echo "Step 1 : Updating packages" printf "\n#--- basic configuration --- \nrpcuser=user\nrpcpassword=$PASS\nrpcport=18889\ndaemon=1\nlisten=1\nserver=1\nmaxconnections=256\nrpcallowip=127.0.0.1\nexternalip=$ext_ip:18888\n" > $conf_path printf "\n#--- masternode ---\nmasternode=1\nmasternodeprivkey=$mn_key\n" >> $conf_path - printf "\n#--- new nodes ---\naddnode=139.99.41.241:18888\naddnode=139.99.41.242:18888\naddnode=139.99.202.1:18888\n" >> $conf_path + printf "\n#--- new nodes ---\naddnode=88.198.95.174:18888\naddnode=135.181.33.18:18888\naddnode=45.77.138.219:18888\naddnode=95.216.209.25:18888\naddnode=116.203.202.68:18888\n" >> $conf_path else printError "Configuration already exist. Remove this file '$conf_path' or configure manyally" diff --git a/doc/masternode/install_ubuntu_1804.sh b/doc/masternode/install_ubuntu_1804.sh deleted file mode 100644 index 4fa4611af6d8..000000000000 --- a/doc/masternode/install_ubuntu_1804.sh +++ /dev/null @@ -1,162 +0,0 @@ -#!/bin/bash - -function printError -{ - printf "\33[0;31m$1\033[0m\n" -} - -function printSuccess -{ - printf "\33[0;32m$1\033[0m\n" - -} - -function printWarning -{ - printf "\33[0;33m$1\033[0m\n" - -} -root_path="$(pwd)" -conf_path="$root_path/.absolutecore/absolute.conf" -ext_ip="" -mn_key=$1 -p_version="" - - -if [ -z $mn_key ]; then - printError "MN key is missing\n Usage $0 \n\n" - exit 0 -fi - -clear -printf "\n\n******* Starting Absolute-Community Masternode installation *******\n\n" -printf " working directory is $(pwd)\n" -{ -echo "Step 1 : Updating packages" - sudo apt-get update -y -qq - sudo apt-get upgrade -y -qq - sudo apt-get install software-properties-common -y -qq - sudo add-apt-repository ppa:bitcoin/bitcoin -y - sudo apt-get update -y -qq - sudo apt-get install nano htop -y -qq - sudo apt-get install pwgen -y -qq - sudo apt-get install libdb4.8-dev libdb4.8++-dev -y -qq - sudo apt-get install tmux -y -qq - sudo apt-get install libevent-pthreads-2.1 -y -qq - sudo apt-get install libboost-all-dev -y -qq - sudo apt-get install libzmq3-dev -y -qq - sudo apt-get install libminiupnpc-dev -y -qq - sudo apt install virtualenv -y -qq - - printf "Detect python version " - p_version="$(python -V)" - - if [ -z $p_version ]; then - sudo apt-get -y -qq install python - fi - - sudo apt-get -y -qq install python-virtualenv - sudo apt-get install git -y -qq - - - #sudo apt-get -y install python-virtualenv -y -qq - echo "changing working folder to $root_path" - cd $root_path - printSuccess "Done" -} || { - printError "Fail" - exit 1 -} - -{ - echo "Step 2 : Downloading binaries - extract" - - if [ ! -f absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz ]; then - echo "Dowloading..." - wget https://github.com/absolute-community/absolute/releases/download/v12.2.5/absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz -O absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz -q - else - printWarning "File already exist" - fi - - if [ ! -d Absolute ]; then - echo "Extracting" - tar -zxvf absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz && - echo "Rename daemon folder" - mv absolutecore-0.12.2.5/bin Absolute - sudo ln -s $root_path/Absolute/absolute-cli /usr/local/bin/absolute-cli - sudo ln -s $root_path/Absolute/absoluted /usr/local/bin/absoluted - else - printWarning "Daemon already exist" - fi - printSuccess "Done" - -} || { - printError "Fail" - exit 1 -} - - -{ - echo "Step 3: Configure" - - if [ ! -f "$conf_path" ]; then - ext_ip=`wget -qO- ipinfo.io/ip` - PASS=`pwgen -1 20 -n` - - mkdir -p "$(dirname "$conf_path")" && - touch $conf_path && - - printf "\n#--- basic configuration --- \nrpcuser=user\nrpcpassword=$PASS\nrpcport=18889\ndaemon=1\nlisten=1\nserver=1\nmaxconnections=256\nrpcallowip=127.0.0.1\nexternalip=$ext_ip:18888\n" > $conf_path - printf "\n#--- masternode ---\nmasternode=1\nmasternodeprivkey=$mn_key\n" >> $conf_path - printf "\n#--- new nodes ---\naddnode=139.99.41.241:18888\naddnode=139.99.41.242:18888\naddnode=139.99.202.1:18888\n" >> $conf_path - - else - printError "Configuration already exist. Remove this file '$conf_path' or configure manyally" - fi - - printSuccess "Done" - - -} || { - - printError "Fail" - exit 1 -} - -{ - echo "Step 4 : Installing Sentinel watch dog" - - if [ ! -d "$root_path/.absolutecore/sentinel" ]; then - cd $root_path/.absolutecore && - git clone https://github.com/absolute-community/sentinel.git --q - fi - - cd $root_path/.absolutecore/sentinel && - virtualenv ./venv && - ./venv/bin/pip install -r requirements.txt - - printSuccess "Done" - -} || { - printError "Fail" - exit 1 -} - -{ - echo "Step 5 : Configuring sentinel" - - if grep -q "absolute_conf=$conf_path" "$root_path/.absolutecore/sentinel/sentinel.conf" ; then - printWarning "absolute path already set in sentinel conf" - else - printf "absolute_conf=$conf_path" >> "$root_path/.absolutecore/sentinel/sentinel.conf" - fi - - printSuccess "Done" -} || { - printError "Fail" - exit 1 -} - -printSuccess "\nInstallation done you can continue to follow last steps from the guide" -printSuccess "\nPlease note this line for your crontab" -printSuccess "\n* * * * * cd $root_path/.absolutecore/sentinel && ./venv/bin/python bin/sentinel.py >/dev/null 2>&1" diff --git a/doc/masternode/masternode_conf.md b/doc/masternode/masternode_conf.md index 745f3bc4c362..acebb094beea 100644 --- a/doc/masternode/masternode_conf.md +++ b/doc/masternode/masternode_conf.md @@ -15,8 +15,8 @@ mn2 127.0.0.4:18800 92Da1aYg6sbenP6uwskJgEY2XWB5LwJ7bXRqc3UPeShtHWJDjDv 5d898e78 ``` In the example above: -* the collateral of 1000 ABS for `mn1` is output `0` of transaction 7603c20a05258c208b58b0a0d77603b9fc93d47cfa403035f87f3ce0af814566 -* the collateral of 1000 ABS for `mn2` is output `1` of transaction 5d898e78244f3206e0105f421cdb071d95d111a51cd88eb5511fc0dbf4bfd95f +* the collateral of 2500 ABS for `mn1` is output `0` of transaction 7603c20a05258c208b58b0a0d77603b9fc93d47cfa403035f87f3ce0af814566 +* the collateral of 2500 ABS for `mn2` is output `1` of transaction 5d898e78244f3206e0105f421cdb071d95d111a51cd88eb5511fc0dbf4bfd95f _Note: IPs like 127.0.0.* are not allowed actually, we are using them here for explanatory purposes only. Make sure you have real reachable remote IPs in you `masternode.conf`._ diff --git a/doc/masternode/update.sh b/doc/masternode/update.sh deleted file mode 100644 index c27e83524640..000000000000 --- a/doc/masternode/update.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -VERSION="absolutecore-0.12.2.5" -echo 'downloading last release'\ -&& wget https://github.com/absolute-community/absolute/releases/download/v12.2.5/absolutecore-0.12.2.5-x86_64-linux-gnu.tar.gz -O absolute_latest.tar.gz \ -&& echo "Extracting" \ -&& tar -zxvf absolute_latest.tar.gz \ -&& echo "Backuping"\ -&& mv "$HOME/Absolute/absolute-cli" "$HOME/Absolute/absolute-cli.bkp" \ -&& mv "$HOME/Absolute/absoluted" "$HOME/Absolute/absoluted.bkp" \ -&& echo "updating" \ -&& mv "$VERSION/bin/absolute-cli" "$HOME/Absolute/absolute-cli" \ -&& mv "$VERSION/bin/absoluted" "$HOME/Absolute/absoluted" \ -&& ln -sf $HOME/Absolute/absolute-cli /usr/local/bin/absolute-cli\ -&& ln -sf $HOME/Absolute/absoluted /usr/local/bin/absoluted\ -&& echo "done" - diff --git a/doc/release-notes.md b/doc/release-notes.md index b8ac7e3414ae..852176951d88 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -10,8 +10,49 @@ Please report bugs using the issue tracker at github: -Upgrading and downgrading -========================= +How to Upgrade +-------------- + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Absolute-Qt (on Mac) or +absoluted/absolute-qt (on Linux). If you upgrade after AIP0003 activation you will +have to reindex (start with -reindex-chainstate or -reindex) to make sure +your wallet has all the new data synced (only if you were using version < 0.12.3). + +When spork15 will be activated on mainnet, there is no need for `masternode start` +anymore. Upgrading a masternode now only involves replacing binaries and restarting +the node. + +Downgrade warning +----------------- + +### Downgrade to a version < 0.12.3.1 + +Downgrading to previous 0.12.2 releases is fully supported but is not recommended unless you have some serious issues with 0.12.3.1. + +Notable changes +=============== + +Number of false-positives from anti virus software should be reduced +-------------------------------------------------------------------- +We have removed all mining code from Windows and Mac binaries, which should avoid most of the false-positive alerts +from anti virus software. Linux builds are not affected. The mining code found in `absolute-qt` and `absoluted` are only meant +for regression/integration tests and povnets, so there is no harm in removing this code from non-linux builds. + +Fixed an issue with invalid merkle blocks causing SPV nodes to ban other nodes +------------------------------------------------------------------------------ +A fix that was introduces in the last minor version caused creation of invalid merkle blocks, which in turn cause SPV +nodes to ban 0.12.3 nodes. This can be observed on mobile clients which have troubles maintaining connections. This +release fixes this issue and should allow SPV/mobile clients to sync with upgraded nodes. + + +Bug fixes/Other improvements +---------------------------- +There are few bug fixes in this release: +- Fixed an issue with transaction sometimes not being fully zapped when `-zapwallettxes` is used +- Fixed an issue with the `protx revoke` RPC and REASON_CHANGE_OF_KEYS + How to Upgrade -------------- @@ -24,15 +65,16 @@ absoluted/absolute-qt (on Linux). Downgrade warning ----------------- -### Downgrade to a version < 0.12.2.2 +### Downgrade to a version < 0.12.3.1 + +Downgrading to a version smaller than 0.12.3.1 is only supported as long as AIP2/AIP3 +has not been activated. Activation will happen when enough miners signal compatibility +through a BIP9 (bit 3) deployment. -Because release 0.12.2.5 included the [per-UTXO fix] -which changed the structure of the internal database, you will have to reindex -the database if you decide to use any pre-0.12.2.2 version. -Wallet forward or backward compatibility was not affected. +byaccount`, `listaccounts`. -### Downgrade to 0.12.2.2 +### Downgrade to 0.12.2.5 Downgrading to 0.12.2.2 does not require any additional actions, should be fully compatible. diff --git a/doc/release-notes/dash/release-notes-0.12.2.2.md b/doc/release-notes/dash/release-notes-0.12.2.2.md index 43bf7420c050..c7e9d6c9bf13 100644 --- a/doc/release-notes/dash/release-notes-0.12.2.2.md +++ b/doc/release-notes/dash/release-notes-0.12.2.2.md @@ -166,8 +166,8 @@ See detailed [change log](https://github.com/dashpay/dash/compare/v0.12.2.1...da - [`4802a1fb7`](https://github.com/dashpay/dash/commit/4802a1fb7) Allow IS for all txes, not only for txes with p2pkh and data outputs (#1760) - [`f37a64208`](https://github.com/dashpay/dash/commit/f37a64208) InstantSend txes should never qualify to be a 0-fee txes (#1777) -### DIP0001: -- [`3028af19f`](https://github.com/dashpay/dash/commit/3028af19f) post-DIP0001 cleanup (#1763) +### AIP0001: +- [`3028af19f`](https://github.com/dashpay/dash/commit/3028af19f) post-AIP0001 cleanup (#1763) - [`51b2c7501`](https://github.com/dashpay/dash/commit/51b2c7501) Fix WarningBitsConditionChecker (#1765) ### Network/Sync: diff --git a/doc/release-notes/dash/release-notes-0.12.3-backports.md b/doc/release-notes/dash/release-notes-0.12.3-backports.md index 5f85a0d3766a..cc899070e36a 100644 --- a/doc/release-notes/dash/release-notes-0.12.3-backports.md +++ b/doc/release-notes/dash/release-notes-0.12.3-backports.md @@ -55,7 +55,7 @@ - [`4959ff201`](https://github.com/dashpay/dash/commit/4959ff201) Fix formatting of help in sendfrom - [`a4430b624`](https://github.com/dashpay/dash/commit/a4430b624) Fix rpc tests - [`5901531bc`](https://github.com/dashpay/dash/commit/5901531bc) Use constant 7200 instead of TIMESTAMP_WINDOW for now -- [`4d3856b5a`](https://github.com/dashpay/dash/commit/4d3856b5a) Include test_dash.h instead of test_bitcoin.h +- [`4d3856b5a`](https://github.com/dashpay/dash/commit/4d3856b5a) Include test_absolute.h instead of test_bitcoin.h - [`d500d77c4`](https://github.com/dashpay/dash/commit/d500d77c4) Use nBlockSize/nBlockMaxSize instead of nBlockWeight/nBlockMaxWeight - [`12770beb6`](https://github.com/dashpay/dash/commit/12770beb6) Merge #10310: [doc] Add hint about getmempoolentry to getrawmempool help. - [`e9976d20f`](https://github.com/dashpay/dash/commit/e9976d20f) Merge #10495: contrib: Update location of seeds.txt @@ -607,7 +607,7 @@ - [`39909d373`](https://github.com/dashpay/dash/commit/39909d373) Merge #8558: Add copyright header to wallet_text_fixture.cpp - [`e2a795fd8`](https://github.com/dashpay/dash/commit/e2a795fd8) Return EXIT_SUCCESS instead of 0 in main() - [`e3da73ebf`](https://github.com/dashpay/dash/commit/e3da73ebf) Update Dash related code to use new SyncTransaction notifications interface -- [`ebc466dbd`](https://github.com/dashpay/dash/commit/ebc466dbd) Revert test_bitcoin.info to test_dash.info in Makefile.am +- [`ebc466dbd`](https://github.com/dashpay/dash/commit/ebc466dbd) Revert test_bitcoin.info to test_absolute.info in Makefile.am - [`f8ffc55af`](https://github.com/dashpay/dash/commit/f8ffc55af) Partially cherry-pick tests refactorings found in Bitcoin SegWit PRs - [`8460a9b97`](https://github.com/dashpay/dash/commit/8460a9b97) Dashify translation_process.md - [`5b62ad34a`](https://github.com/dashpay/dash/commit/5b62ad34a) Merge #8551: [qa] Remove unused code diff --git a/doc/release-notes/dash/release-notes-0.12.3.1.md b/doc/release-notes/dash/release-notes-0.12.3.1.md index 749a368f21d8..d31fe0776453 100644 --- a/doc/release-notes/dash/release-notes-0.12.3.1.md +++ b/doc/release-notes/dash/release-notes-0.12.3.1.md @@ -399,7 +399,7 @@ See detailed [change log](https://github.com/dashpay/dash/compare/v0.12.2.3...da - [`a9d8e2c5d`](https://github.com/dashpay/dash/commit/a9d8e2c5d) [Init] Avoid segfault when called with -enableinstantsend=0 (#1976) - [`3200eae9b`](https://github.com/dashpay/dash/commit/3200eae9b) Don't use short version of 'tinyformat/fmt' namespace in util.h (#1975) - [`97a07cbc4`](https://github.com/dashpay/dash/commit/97a07cbc4) Refactor `CMasternodePayment*` (#1974) -- [`4ffa7bac0`](https://github.com/dashpay/dash/commit/4ffa7bac0) Introduce DIP0001Height (#1973) +- [`4ffa7bac0`](https://github.com/dashpay/dash/commit/4ffa7bac0) Introduce AIP0001Height (#1973) - [`611879aa6`](https://github.com/dashpay/dash/commit/611879aa6) Use spork addresses instead of raw keys and allow changing them on startup (#1969) - [`9ef38c6d7`](https://github.com/dashpay/dash/commit/9ef38c6d7) Switch CNetFulfilledRequestManager and CMasternodeMan maps/funcs to CService (#1967) - [`929c1584a`](https://github.com/dashpay/dash/commit/929c1584a) Rename CheckPreviousBlockVotes to CheckBlockVotes and adjust its log output a bit (#1965) @@ -427,10 +427,10 @@ See detailed [change log](https://github.com/dashpay/dash/compare/v0.12.2.3...da - [`9cee4193b`](https://github.com/dashpay/dash/commit/9cee4193b) Separate .h generation from .json/.raw for different modules (#1870) - [`83957f2d3`](https://github.com/dashpay/dash/commit/83957f2d3) Fix alertTests.raw.h (again) (#1869) - [`c13afaad8`](https://github.com/dashpay/dash/commit/c13afaad8) Fix alertTests.raw.h generation (#1868) -- [`a46bf120b`](https://github.com/dashpay/dash/commit/a46bf120b) Don't directly call "wine test_dash.exe" and let "make check" handle it (#1841) +- [`a46bf120b`](https://github.com/dashpay/dash/commit/a46bf120b) Don't directly call "wine test_absolute.exe" and let "make check" handle it (#1841) - [`e805f790e`](https://github.com/dashpay/dash/commit/e805f790e) Automatically build and push docker image to docker.io/dashpay/dashd-develop (#1809) -- [`d9058aa04`](https://github.com/dashpay/dash/commit/d9058aa04) Increase travis timeout for "wine src/test/test_dash.exe" call (#1820) -- [`10786fe8e`](https://github.com/dashpay/dash/commit/10786fe8e) Use travis_wait for "wine test_dash.exe" call to fix timeouts (#1812) +- [`d9058aa04`](https://github.com/dashpay/dash/commit/d9058aa04) Increase travis timeout for "wine src/test/test_absolute.exe" call (#1820) +- [`10786fe8e`](https://github.com/dashpay/dash/commit/10786fe8e) Use travis_wait for "wine test_absolute.exe" call to fix timeouts (#1812) - [`4bce3bf8b`](https://github.com/dashpay/dash/commit/4bce3bf8b) Fix crash on exit when -createwalletbackups=0 (#1810) - [`cd9c6994c`](https://github.com/dashpay/dash/commit/cd9c6994c) Implement named devnets (#1791) - [`ebbd26a05`](https://github.com/dashpay/dash/commit/ebbd26a05) Drop IsInputAssociatedWithPubkey and optimize CheckOutpoint (#1783) diff --git a/doc/release-process.md b/doc/release-process.md index 1229f0c59a10..8b4b4b423d55 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -21,7 +21,8 @@ Before every major release: ### First time / New builders -If you're using the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)), then at this point you should run it with the "--setup" command. Otherwise ignore this. +If you're using the automated script (found in [contrib/gitian-build.py](/contrib/gitian-build.py)), then at this point you should run it with the "--setup" command. Otherwise ignore this. + Check out the source code in the following directory hierarchy. cd /path/to/your/toplevel/build @@ -60,7 +61,7 @@ Tag version (or release candidate) in git ### Setup and perform Gitian builds -If you're using the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)), then at this point you should run it with the "--build" command. Otherwise ignore this. +If you're using the automated script (found in [contrib/gitian-build.py](/contrib/gitian-build.py)), then at this point you should run it with the "--build" command. Otherwise ignore this. Setup Gitian descriptors: diff --git a/doc/zmq.md b/doc/zmq.md index a5f695801141..4126b6348ceb 100644 --- a/doc/zmq.md +++ b/doc/zmq.md @@ -62,6 +62,12 @@ Currently, the following notifications are supported: -zmqpubrawblock=address -zmqpubrawtx=address -zmqpubrawtxlock=address + -zmqpubhashgovernancevote=address + -zmqpubhashgovernanceobject=address + -zmqpubrawgovernancevote=address + -zmqpubhashgovernanceobject=address + -zmqpubrawinstantsenddoublespend=address + -zmqpubhashinstantsenddoublespend=address The socket type is PUB and the address must be a valid ZeroMQ socket address. The same address can be used in more than one notification. diff --git a/docker/README.md b/docker/README.md index b3cee51225a7..740e1b6fa748 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,8 +1,8 @@ This docker image is built and pushed from travis. It does not contain any default configs or any special startup scripts. This is to keep it simple and to not copy too much logic from the -official/supported Docker Images found at https://hub.docker.com/r/absolutecoin/absolute/ +official/supported Docker Images found at https://hub.docker.com/r/absolutecommunity/docker-absoluted/ The image is mostly used for devops purposes, e.g. for testnet/povnet deployments. Regular users should use the official images instead of this one. -**NOTE: Please update the image description at https://hub.docker.com/r/absolutecoin/absolute/ when changing the above text** +**NOTE: Please update the image description at https://hub.docker.com/r/absolutecommunity/docker-absoluted/ when changing the above text** diff --git a/docker/build-docker.sh b/docker/build-docker.sh old mode 100644 new mode 100755 index fdb205ea9380..54b5015a556f --- a/docker/build-docker.sh +++ b/docker/build-docker.sh @@ -3,7 +3,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd $DIR/.. -DOCKER_IMAGE=${DOCKER_IMAGE:-absolutecoin/absoluted-develop} +DOCKER_IMAGE=${DOCKER_IMAGE:-absolutecommunity/docker-absoluted} DOCKER_TAG=${DOCKER_TAG:-latest} BUILD_DIR=${BUILD_DIR:-.} diff --git a/docker/push-docker.sh b/docker/push-docker.sh old mode 100644 new mode 100755 index 23b235f75096..a9148ee7065c --- a/docker/push-docker.sh +++ b/docker/push-docker.sh @@ -3,7 +3,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd $DIR/.. -DOCKER_IMAGE=${DOCKER_IMAGE:-absolutecoin/absoluted-develop} +DOCKER_IMAGE=${DOCKER_IMAGE:-absolutecommunity/docker-absoluted} DOCKER_TAG=${DOCKER_TAG:-latest} if [ -n "$DOCKER_REPO" ]; then diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 6a8997375dc0..76080ac07f4a 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -50,7 +50,7 @@ if 'ENABLE_ZMQ' not in vars(): ENABLE_ZMQ=0 - + # python-zmq may not be installed. Handle this gracefully and with some helpful info if ENABLE_ZMQ: try: @@ -110,17 +110,22 @@ testScripts = [ # longest test should go first, to favor running tests in parallel + 'aip3-deterministicmns.py', # NOTE: needs dash_hash to pass 'wallet-hd.py', 'walletbackup.py', # vv Tests less than 5m vv 'p2p-fullblocktest.py', # NOTE: needs absolute_hash to pass 'fundrawtransaction.py', 'fundrawtransaction-hd.py', + 'p2p-autoinstantsend.py', + 'autoix-mempool.py', # vv Tests less than 2m vv + 'p2p-instantsend.py', 'wallet.py', 'wallet-accounts.py', 'wallet-dump.py', 'listtransactions.py', + 'multikeysporks.py', # vv Tests less than 60s vv 'sendheaders.py', # NOTE: needs absolute_hash to pass 'zapwallettxes.py', @@ -167,6 +172,7 @@ 'listsinceblock.py', 'p2p-leaktests.py', 'p2p-compactblocks.py', + 'sporks.py', ] if ENABLE_ZMQ: testScripts.append('zmq_test.py') @@ -196,8 +202,7 @@ 'forknotify.py', 'invalidateblock.py', 'maxblocksinflight.py', - 'p2p-acceptblock.py', # NOTE: needs absolute_hash to pass - # 'replace-by-fee.py', # RBF is disabled in absolute Core + 'p2p-acceptblock.py', # NOTE: needs dash_hash to pass ] diff --git a/qa/rpc-tests/aip3-deterministicmns.py b/qa/rpc-tests/aip3-deterministicmns.py new file mode 100644 index 000000000000..31d1541c8b93 --- /dev/null +++ b/qa/rpc-tests/aip3-deterministicmns.py @@ -0,0 +1,882 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test deterministic masternodes +# + +from test_framework.blocktools import create_block, create_coinbase, get_masternode_payment +from test_framework.mininode import CTransaction, ToHex, FromHex, CTxOut, COIN, CCbTx +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +class Masternode(object): + pass + +class AIP3Test(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_initial_mn = 11 # Should be >= 11 to make sure quorums are not always the same MNs + self.num_nodes = 1 + self.num_initial_mn + 2 # +1 for controller, +1 for mn-qt, +1 for mn created after aip3 activation + self.setup_clean_chain = True + + self.extra_args = ["-budgetparams=240:100:240"] + self.extra_args += ["-sporkkey=cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"] + + def setup_network(self): + disable_mocktime() + self.start_controller_node() + self.is_network_split = False + + def start_controller_node(self, extra_args=None): + print("starting controller node") + if self.nodes is None: + self.nodes = [None] + args = self.extra_args + if extra_args is not None: + args += extra_args + self.nodes[0] = start_node(0, self.options.tmpdir, extra_args=args) + for i in range(1, self.num_nodes): + if i < len(self.nodes) and self.nodes[i] is not None: + connect_nodes_bi(self.nodes, 0, i) + + def stop_controller_node(self): + print("stopping controller node") + stop_node(self.nodes[0], 0) + + def restart_controller_node(self): + self.stop_controller_node() + self.start_controller_node() + + def run_test(self): + print("funding controller node") + while self.nodes[0].getbalance() < (self.num_initial_mn + 3) * 1000: + self.nodes[0].generate(1) # generate enough for collaterals + print("controller node has {} absolute".format(self.nodes[0].getbalance())) + + # Make sure we're below block 143 (which activates aip3) + print("testing rejection of ProTx before aip3 activation") + assert(self.nodes[0].getblockchaininfo()['blocks'] < 143) + aip3_deployment = self.nodes[0].getblockchaininfo()['bip9_softforks']['aip0003'] + assert_equal(aip3_deployment['status'], 'defined') + + self.test_fail_create_protx(self.nodes[0]) + + mns = [] + mn_idx = 1 + for i in range(self.num_initial_mn): + mn = self.create_mn(self.nodes[0], mn_idx, 'mn-%d' % (mn_idx)) + mn_idx += 1 + mns.append(mn) + + # mature collaterals + for i in range(3): + self.nodes[0].generate(1) + time.sleep(1) + + self.write_mnconf(mns) + + self.restart_controller_node() + for mn in mns: + self.start_mn(mn) + self.sync_all() + + # force finishing of mnsync + for node in self.nodes: + self.force_finish_mnsync(node) + + # start MNs + print("start mns") + for mn in mns: + self.start_alias(self.nodes[0], mn.alias) + print("wait for MNs to appear in MN lists") + self.wait_for_mnlists(mns, True, False) + + print("testing MN payment votes") + self.test_mn_votes(10) + + print("testing instant send") + self.test_instantsend(10, 5) + + print("testing rejection of ProTx before aip3 activation (in states defined, started and locked_in)") + while self.nodes[0].getblockchaininfo()['bip9_softforks']['aip0003']['status'] == 'defined': + self.nodes[0].generate(1) + self.test_fail_create_protx(self.nodes[0]) + while self.nodes[0].getblockchaininfo()['bip9_softforks']['aip0003']['status'] == 'started': + self.nodes[0].generate(1) + self.test_fail_create_protx(self.nodes[0]) + + # prepare mn which should still be accepted later when aip3 activates + print("creating collateral for mn-before-aip3") + before_aip3_mn = self.create_mn(self.nodes[0], mn_idx, 'mn-before-aip3') + mn_idx += 1 + + while self.nodes[0].getblockchaininfo()['bip9_softforks']['aip0003']['status'] == 'locked_in': + self.nodes[0].generate(1) + + # We have hundreds of blocks to sync here, give it more time + print("syncing blocks for all nodes") + sync_blocks(self.nodes, timeout=120) + + # AIP3 has activated here + + print("testing rejection of ProTx right before aip3 activation") + best_block = self.nodes[0].getbestblockhash() + self.nodes[0].invalidateblock(best_block) + self.test_fail_create_protx(self.nodes[0]) + self.nodes[0].reconsiderblock(best_block) + + # Now it should be possible to mine ProTx + self.sync_all() + self.test_success_create_protx(self.nodes[0]) + + print("creating collateral for mn-after-aip3") + after_aip3_mn = self.create_mn(self.nodes[0], mn_idx, 'mn-after-aip3') + # mature collaterals + for i in range(3): + self.nodes[0].generate(1) + time.sleep(1) + + print("testing if we can start a mn which was created before aip3 activation") + self.write_mnconf(mns + [before_aip3_mn, after_aip3_mn]) + self.restart_controller_node() + self.force_finish_mnsync(self.nodes[0]) + + print("start MN %s" % before_aip3_mn.alias) + mns.append(before_aip3_mn) + self.start_mn(before_aip3_mn) + self.wait_for_sporks() + self.force_finish_mnsync_list(before_aip3_mn.node) + self.start_alias(self.nodes[0], before_aip3_mn.alias) + + self.wait_for_mnlists(mns) + self.wait_for_mnlists_same() + + # Test if nodes still allow creating new non-ProTx MNs now + print("testing if MN start succeeds when using collateral which was created after aip3 activation") + print("start MN %s" % after_aip3_mn.alias) + mns.append(after_aip3_mn) + self.start_mn(after_aip3_mn) + self.wait_for_sporks() + self.force_finish_mnsync_list(after_aip3_mn.node) + self.start_alias(self.nodes[0], after_aip3_mn.alias) + + self.wait_for_mnlists(mns) + self.wait_for_mnlists_same() + + first_upgrade_count = 5 + mns_after_upgrade = [] + mns_to_restart = [] + mns_to_restart_later = [] + mns_protx = [] + print("upgrading first %d MNs to use ProTx (but not deterministic MN lists)" % first_upgrade_count) + for i in range(first_upgrade_count): + # let a few of the protx MNs refer to the old collaterals + fund = (i % 2) == 0 + mns[i] = self.upgrade_mn_protx(mns[i], fund) + self.nodes[0].generate(1) + + if fund: + # collateral has moved, so we need to start it again + mns_to_restart.append(mns[i]) + else: + # collateral has not moved, so it should still be in the masternode list even after upgrade + mns_after_upgrade.append(mns[i]) + mns_to_restart_later.append(mns[i]) + mns_protx.append(mns[i]) + for i in range(first_upgrade_count, len(mns)): + mns_after_upgrade.append(mns[i]) + self.write_mnconf(mns) + + print("wait for freshly funded and upgraded MNs to disappear from MN lists (their collateral was spent)") + self.wait_for_mnlists(mns_after_upgrade, check=True) + self.wait_for_mnlists_same() + + print("restarting controller and upgraded MNs") + self.restart_controller_node() + self.force_finish_mnsync_list(self.nodes[0]) + for mn in mns_to_restart: + print("restarting MN %s" % mn.alias) + self.stop_node(mn.idx) + self.start_mn(mn) + self.force_finish_mnsync_list(mn.node) + print('start-alias on upgraded nodes') + for mn in mns_to_restart: + self.start_alias(self.nodes[0], mn.alias) + + print("wait for upgraded MNs to appear in MN list") + self.wait_for_mnlists(mns) + self.wait_for_mnlists_same() + + print("testing MN payment votes (with mixed ProTx and legacy nodes)") + self.test_mn_votes(10, test_enforcement=True) + + print("testing instant send (with mixed ProTx and legacy nodes)") + self.test_instantsend(10, 3) + + # We still need to restart them as otherwise they won't have the BLS operator key loaded + print("restart upgraded nodes which refer to old collaterals") + for mn in mns_to_restart_later: + print("restarting MN %s" % mn.alias) + self.stop_node(mn.idx) + self.start_mn(mn) + self.force_finish_mnsync_list(mn.node) + + print("activating spork15") + height = self.nodes[0].getblockchaininfo()['blocks'] + spork15_offset = 10 + self.nodes[0].spork('SPORK_15_DETERMINISTIC_MNS_ENABLED', height + spork15_offset) + self.wait_for_sporks() + + print("test that MN list does not change before final spork15 activation") + for i in range(spork15_offset - 1): + self.nodes[0].generate(1) + self.sync_all() + self.wait_for_mnlists(mns) + self.wait_for_mnlists_same() + + print("mining final block which should switch network to deterministic lists") + self.nodes[0].generate(1) + self.sync_all() + + ##### WOW...we made it...we are in deterministic MN lists mode now. + ##### From now on, we don't wait for mnlists to become correct anymore, we always assert that they are correct immediately + + print("assert that not upgraded MNs disappeared from MN list") + self.assert_mnlists(mns_protx) + + # enable enforcement and keep it on from now on + self.nodes[0].spork('SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT', 0) + self.wait_for_sporks() + + print("test that MNs disappear from the list when the ProTx collateral is spent") + spend_mns_count = 3 + mns_tmp = [] + mns_protx + dummy_txins = [] + for i in range(spend_mns_count): + dummy_txin = self.spend_mn_collateral(mns_protx[i], with_dummy_input_output=True) + dummy_txins.append(dummy_txin) + self.nodes[0].generate(1) + self.sync_all() + mns_tmp.remove(mns_protx[i]) + self.assert_mnlists(mns_tmp) + + print("test that reverting the blockchain on a single node results in the mnlist to be reverted as well") + for i in range(spend_mns_count): + self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) + mns_tmp.append(mns_protx[spend_mns_count - 1 - i]) + self.assert_mnlist(self.nodes[0], mns_tmp) + + print("cause a reorg with a double spend and check that mnlists are still correct on all nodes") + self.mine_double_spend(self.nodes[0], dummy_txins, self.nodes[0].getnewaddress(), use_mnmerkleroot_from_tip=True) + self.nodes[0].generate(spend_mns_count) + self.sync_all() + self.assert_mnlists(mns_tmp) + + print("upgrade remaining MNs to ProTx") + for i in range(first_upgrade_count, len(mns)): + mns[i] = self.upgrade_mn_protx(mns[i], True) + mn = mns[i] + self.nodes[0].generate(1) + mns_protx.append(mn) + print("restarting MN %s" % mn.alias) + self.stop_node(mn.idx) + self.start_mn(mn) + self.sync_all() + self.force_finish_mnsync(mn.node) + self.assert_mnlists(mns_protx) + + self.assert_mnlists(mns_protx) + + print("test mn payment enforcement with deterministic MNs") + for i in range(20): + node = self.nodes[i % len(self.nodes)] + self.test_invalid_mn_payment(node) + node.generate(1) + self.sync_all() + + print("testing instant send with deterministic MNs") + self.test_instantsend(10, 5, timeout=20) + + print("testing ProUpServTx") + for mn in mns_protx: + self.test_protx_update_service(mn) + + print("testing P2SH/multisig for payee addresses") + multisig = self.nodes[0].createmultisig(1, [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()])['address'] + self.update_mn_payee(mns_protx[0], multisig) + found_multisig_payee = False + for i in range(len(mns_protx)): + bt = self.nodes[0].getblocktemplate() + expected_payee = bt['masternode'][0]['payee'] + expected_amount = bt['masternode'][0]['amount'] + self.nodes[0].generate(1) + self.sync_all() + if expected_payee == multisig: + block = self.nodes[0].getblock(self.nodes[0].getbestblockhash()) + cbtx = self.nodes[0].getrawtransaction(block['tx'][0], 1) + for out in cbtx['vout']: + if 'addresses' in out['scriptPubKey']: + if expected_payee in out['scriptPubKey']['addresses'] and out['valueSat'] == expected_amount: + found_multisig_payee = True + assert(found_multisig_payee) + + print("testing reusing of collaterals for replaced MNs") + for i in range(0, 5): + mn = mns_protx[i] + # a few of these will actually refer to old ProRegTx internal collaterals, + # which should work the same as external collaterals + mn = self.create_mn_protx(self.nodes[0], mn.idx, 'mn-protx-%d' % mn.idx, mn.collateral_txid, mn.collateral_vout) + mns_protx[i] = mn + self.nodes[0].generate(1) + self.sync_all() + self.assert_mnlists(mns_protx) + print("restarting MN %s" % mn.alias) + self.stop_node(mn.idx) + self.start_mn(mn) + self.sync_all() + + print("testing instant send with replaced MNs") + self.test_instantsend(10, 3, timeout=20) + + print("testing simple PoSe") + self.assert_mnlists(mns_protx) + self.nodes[0].spork('SPORK_17_QUORUM_DKG_ENABLED', 0) + self.wait_for_sporks() + + height = self.nodes[0].getblockcount() + skip_count = 24 - (height % 24) + if skip_count != 0: + self.nodes[0].generate(skip_count) + + for i in range(len(mns_protx), len(mns_protx) - 2, -1): + mn = mns_protx[len(mns_protx) - 1] + mns_protx.remove(mn) + self.stop_node(mn.idx) + self.nodes.remove(mn.node) + + punished = False + banned = False + t = time.time() + while (not punished or not banned) and (time.time() - t) < 120: + time.sleep(1) + + # 10 blocks until we can mine the dummy commitment + for j in range(10): + self.nodes[0].generate(1) + self.sync_all() + time.sleep(0.5) + + info = self.nodes[0].protx('info', mn.protx_hash) + if not punished: + if info['state']['PoSePenalty'] > 0: + punished = True + if not banned: + if info['state']['PoSeBanHeight'] != -1: + banned = True + + # Fast-forward to next DKG session + self.nodes[0].generate(24 - (self.nodes[0].getblockcount() % 24)) + self.sync_all() + assert(punished and banned) + + def create_mn(self, node, idx, alias): + mn = Masternode() + mn.idx = idx + mn.alias = alias + mn.is_protx = False + mn.p2p_port = p2p_port(mn.idx) + + blsKey = node.bls('generate') + mn.legacyMnkey = node.masternode('genkey') + mn.blsMnkey = blsKey['secret'] + mn.collateral_address = node.getnewaddress() + mn.collateral_txid = node.sendtoaddress(mn.collateral_address, 1000) + rawtx = node.getrawtransaction(mn.collateral_txid, 1) + + mn.collateral_vout = -1 + for txout in rawtx['vout']: + if txout['value'] == Decimal(1000): + mn.collateral_vout = txout['n'] + break + assert(mn.collateral_vout != -1) + + lock = node.lockunspent(False, [{'txid': mn.collateral_txid, 'vout': mn.collateral_vout}]) + + return mn + + def create_mn_protx_base(self, node, idx, alias, legacy_mn_key): + mn = Masternode() + mn.idx = idx + mn.alias = alias + mn.is_protx = True + mn.p2p_port = p2p_port(mn.idx) + + blsKey = node.bls('generate') + mn.fundsAddr = node.getnewaddress() + mn.ownerAddr = node.getnewaddress() + mn.operatorAddr = blsKey['public'] + mn.votingAddr = mn.ownerAddr + mn.legacyMnkey = node.masternode('genkey') if legacy_mn_key is None else legacy_mn_key + mn.blsMnkey = blsKey['secret'] + + return mn + + # create a protx MN and also fund it (using collateral inside ProRegTx) + def create_mn_protx_fund(self, node, idx, alias, legacy_mn_key=None): + mn = self.create_mn_protx_base(node, idx, alias, legacy_mn_key=legacy_mn_key) + node.sendtoaddress(mn.fundsAddr, 1000.001) + + mn.collateral_address = node.getnewaddress() + + mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.collateral_address, mn.fundsAddr) + mn.collateral_txid = mn.protx_hash + mn.collateral_vout = -1 + + rawtx = node.getrawtransaction(mn.collateral_txid, 1) + for txout in rawtx['vout']: + if txout['value'] == Decimal(1000): + mn.collateral_vout = txout['n'] + break + assert(mn.collateral_vout != -1) + + return mn + + # create a protx MN which refers to an existing collateral + def create_mn_protx(self, node, idx, alias, collateral_txid, collateral_vout, legacy_mn_key=None): + mn = self.create_mn_protx_base(node, idx, alias, legacy_mn_key=legacy_mn_key) + node.sendtoaddress(mn.fundsAddr, 0.001) + + mn.rewards_address = node.getnewaddress() + + mn.protx_hash = node.protx('register', collateral_txid, collateral_vout, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr) + mn.collateral_txid = collateral_txid + mn.collateral_vout = collateral_vout + + return mn + + def start_mn(self, mn): + while len(self.nodes) <= mn.idx: + self.nodes.append(None) + extra_args = ['-masternode=1', '-masternodeprivkey=%s' % mn.legacyMnkey, '-masternodeblsprivkey=%s' % mn.blsMnkey] + n = start_node(mn.idx, self.options.tmpdir, self.extra_args + extra_args, redirect_stderr=True) + self.nodes[mn.idx] = n + for i in range(0, self.num_nodes): + if i < len(self.nodes) and self.nodes[i] is not None and i != mn.idx: + connect_nodes_bi(self.nodes, mn.idx, i) + mn.node = self.nodes[mn.idx] + self.sync_all() + + def spend_mn_collateral(self, mn, with_dummy_input_output=False): + return self.spend_input(mn.collateral_txid, mn.collateral_vout, 1000, with_dummy_input_output) + + def upgrade_mn_protx(self, mn, refund): + if refund: + self.spend_mn_collateral(mn) + mn = self.create_mn_protx_fund(self.nodes[0], mn.idx, 'mn-protx-%d' % mn.idx, legacy_mn_key=mn.legacyMnkey) + else: + mn = self.create_mn_protx(self.nodes[0], mn.idx, 'mn-protx-%d' % mn.idx, mn.collateral_txid, mn.collateral_vout, legacy_mn_key=mn.legacyMnkey) + return mn + + def update_mn_payee(self, mn, payee): + self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001) + self.nodes[0].protx('update_registrar', mn.protx_hash, '', '', payee, mn.fundsAddr) + self.nodes[0].generate(1) + self.sync_all() + info = self.nodes[0].protx('info', mn.protx_hash) + assert(info['state']['payoutAddress'] == payee) + + def test_protx_update_service(self, mn): + self.nodes[0].sendtoaddress(mn.fundsAddr, 0.001) + self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.2:%d' % mn.p2p_port, mn.blsMnkey, "", mn.fundsAddr) + self.nodes[0].generate(1) + self.sync_all() + for node in self.nodes: + protx_info = node.protx('info', mn.protx_hash) + mn_list = node.masternode('list') + assert_equal(protx_info['state']['addr'], '127.0.0.2:%d' % mn.p2p_port) + assert_equal(mn_list['%s-%d' % (mn.collateral_txid, mn.collateral_vout)]['address'], '127.0.0.2:%d' % mn.p2p_port) + + # undo + self.nodes[0].protx('update_service', mn.protx_hash, '127.0.0.1:%d' % mn.p2p_port, mn.blsMnkey, "", mn.fundsAddr) + self.nodes[0].generate(1) + + def force_finish_mnsync(self, node): + while True: + s = node.mnsync('next') + if s == 'sync updated to MASTERNODE_SYNC_FINISHED': + break + time.sleep(0.1) + + def force_finish_mnsync_list(self, node): + if node.mnsync('status')['AssetName'] == 'MASTERNODE_SYNC_WAITING': + node.mnsync('next') + + while True: + mnlist = node.masternode('list', 'status') + if len(mnlist) != 0: + time.sleep(0.5) + self.force_finish_mnsync(node) + return + time.sleep(0.1) + + def write_mnconf_line(self, mn, f): + conf_line = "%s %s:%d %s %s %d\n" % (mn.alias, '127.0.0.1', mn.p2p_port, mn.legacyMnkey, mn.collateral_txid, mn.collateral_vout) + f.write(conf_line) + + def write_mnconf(self, mns): + mnconf_file = os.path.join(self.options.tmpdir, "node0/regtest/masternode.conf") + with open(mnconf_file, 'w') as f: + for mn in mns: + self.write_mnconf_line(mn, f) + + def start_alias(self, node, alias, should_fail=False): + # When generating blocks very fast, the logic in miner.cpp:UpdateTime might result in block times ahead of the real time + # This can easily accumulate to 30 seconds or more, which results in start-alias to fail as it expects the sigTime + # to be less or equal to the confirmation block time + # Solution is to sleep in this case. + lastblocktime = node.getblock(node.getbestblockhash())['time'] + sleeptime = lastblocktime - time.time() + if sleeptime > 0: + time.sleep(sleeptime + 1) # +1 to be extra sure + + start_result = node.masternode('start-alias', alias) + if not should_fail: + assert_equal(start_result, {'result': 'successful', 'alias': alias}) + else: + assert_equal(start_result, {'result': 'failed', 'alias': alias, 'errorMessage': 'Failed to verify MNB'}) + + def generate_blocks_until_winners(self, node, count, timeout=60): + # Winner lists are pretty much messed up when too many blocks were generated in a short time + # To allow proper testing of winners list, we need to slowly generate a few blocks until the list stabilizes + good_count = 0 + st = time.time() + while time.time() < st + timeout: + height = node.getblockchaininfo()['blocks'] + 10 + winners = node.masternode('winners') + if str(height) in winners: + if re.match('[0-9a-zA-Z]*:10', winners[str(height)]): + good_count += 1 + if good_count >= count: + return + else: + good_count = 0 + node.generate(1) + self.sync_all() + time.sleep(1) + raise AssertionError("generate_blocks_until_winners timed out: {}".format(node.masternode('winners'))) + + def test_mn_votes(self, block_count, test_enforcement=False): + self.generate_blocks_until_winners(self.nodes[0], self.num_nodes) + + if test_enforcement: + self.nodes[0].spork('SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT', 0) + self.wait_for_sporks() + self.test_invalid_mn_payment(self.nodes[0]) + + cur_block = 0 + while cur_block < block_count: + for n1 in self.nodes: + if cur_block >= block_count: + break + if n1 is None: + continue + + if test_enforcement: + self.test_invalid_mn_payment(n1) + + n1.generate(1) + cur_block += 1 + self.sync_all() + + height = n1.getblockchaininfo()['blocks'] + winners = self.wait_for_winners(n1, height + 10) + + for n2 in self.nodes: + if n1 is n2 or n2 is None: + continue + winners2 = self.wait_for_winners(n2, height + 10) + if winners[str(height + 10)] != winners2[str(height + 10)]: + print("winner1: " + str(winners[str(height + 10)])) + print("winner2: " + str(winners2[str(height + 10)])) + raise AssertionError("winners did not match") + + if test_enforcement: + self.nodes[0].spork('SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT', 4070908800) + + def test_instantsend(self, tx_count, repeat, timeout=20): + self.nodes[0].spork('SPORK_2_INSTANTSEND_ENABLED', 0) + self.wait_for_sporks() + + # give all nodes some coins first + for i in range(tx_count): + outputs = {} + for node in self.nodes[1:]: + outputs[node.getnewaddress()] = 1 + rawtx = self.nodes[0].createrawtransaction([], outputs) + rawtx = self.nodes[0].fundrawtransaction(rawtx)['hex'] + rawtx = self.nodes[0].signrawtransaction(rawtx)['hex'] + self.nodes[0].sendrawtransaction(rawtx) + self.nodes[0].generate(1) + self.sync_all() + + for j in range(repeat): + for i in range(tx_count): + while True: + from_node_idx = random.randint(0, len(self.nodes) - 1) + from_node = self.nodes[from_node_idx] + if from_node is not None: + break + while True: + to_node_idx = random.randint(0, len(self.nodes) - 1) + to_node = self.nodes[to_node_idx] + if to_node is not None and from_node is not to_node: + break + to_address = to_node.getnewaddress() + txid = from_node.instantsendtoaddress(to_address, 0.01) + for node in self.nodes: + if node is not None: + self.wait_for_instant_lock(node, to_node_idx, txid, timeout=timeout) + self.nodes[0].generate(6) + self.sync_all() + + def wait_for_instant_lock(self, node, node_idx, txid, timeout=10): + st = time.time() + while time.time() < st + timeout: + try: + tx = node.getrawtransaction(txid, 1) + except: + tx = None + if tx is None: + time.sleep(0.5) + continue + if tx['instantlock']: + return + time.sleep(0.5) + raise AssertionError("wait_for_instant_lock timed out for: {} on node {}".format(txid, node_idx)) + + def wait_for_winners(self, node, height, timeout=5): + st = time.time() + while time.time() < st + timeout: + winners = node.masternode('winners') + if str(height) in winners: + if re.match('[0-9a-zA-Z]*:10', winners[str(height)]): + return winners + time.sleep(0.5) + raise AssertionError("wait_for_winners for height {} timed out: {}".format(height, node.masternode('winners'))) + + def wait_for_mnlists(self, mns, timeout=30, check=False): + for node in self.nodes: + self.wait_for_mnlist(node, mns, timeout, check=check) + + def wait_for_mnlist(self, node, mns, timeout=30, check=False): + st = time.time() + while time.time() < st + timeout: + if check: + node.masternode('check') + if self.compare_mnlist(node, mns): + return + time.sleep(0.5) + raise AssertionError("wait_for_mnlist timed out") + + def assert_mnlists(self, mns): + for node in self.nodes: + self.assert_mnlist(node, mns) + + def assert_mnlist(self, node, mns): + if not self.compare_mnlist(node, mns): + expected = [] + for mn in mns: + expected.append('%s-%d' % (mn.collateral_txid, mn.collateral_vout)) + print('mnlist: ' + str(node.masternode('list', 'status'))) + print('expected: ' + str(expected)) + raise AssertionError("mnlists does not match provided mns") + + def wait_for_sporks(self, timeout=30): + st = time.time() + while time.time() < st + timeout: + if self.compare_sporks(): + return + time.sleep(0.5) + raise AssertionError("wait_for_sporks timed out") + + def compare_sporks(self): + sporks = self.nodes[0].spork('show') + for node in self.nodes[1:]: + sporks2 = node.spork('show') + if sporks != sporks2: + return False + return True + + def compare_mnlist(self, node, mns): + mnlist = node.masternode('list', 'status') + for mn in mns: + s = '%s-%d' % (mn.collateral_txid, mn.collateral_vout) + in_list = s in mnlist + if not in_list: + return False + mnlist.pop(s, None) + if len(mnlist) != 0: + return False + return True + + def wait_for_mnlists_same(self, timeout=30): + st = time.time() + while time.time() < st + timeout: + mnlist = self.nodes[0].masternode('list', 'status') + all_match = True + for node in self.nodes[1:]: + mnlist2 = node.masternode('list', 'status') + if mnlist != mnlist2: + all_match = False + break + if all_match: + return + time.sleep(0.5) + raise AssertionError("wait_for_mnlists_same timed out") + + def test_fail_create_protx(self, node): + # Try to create ProTx (should still fail) + fund_address = node.getnewaddress() + address = node.getnewaddress() + node.sendtoaddress(fund_address, 1000.001) # +0.001 for fees + key = node.getnewaddress() + blsKey = node.bls('generate') + assert_raises_jsonrpc(None, "bad-tx-type", node.protx, 'register_fund', address, '127.0.0.1:10000', key, blsKey['public'], key, 0, address, fund_address) + + def test_success_create_protx(self, node): + fund_address = node.getnewaddress() + address = node.getnewaddress() + txid = node.sendtoaddress(fund_address, 1000.001) # +0.001 for fees + key = node.getnewaddress() + blsKey = node.bls('generate') + node.protx('register_fund', address, '127.0.0.1:10000', key, blsKey['public'], key, 0, address, fund_address) + rawtx = node.getrawtransaction(txid, 1) + self.mine_double_spend(node, rawtx['vin'], address, use_mnmerkleroot_from_tip=True) + self.sync_all() + + def spend_input(self, txid, vout, amount, with_dummy_input_output=False): + # with_dummy_input_output is useful if you want to test reorgs with double spends of the TX without touching the actual txid/vout + address = self.nodes[0].getnewaddress() + target = {address: amount} + if with_dummy_input_output: + dummyaddress = self.nodes[0].getnewaddress() + target[dummyaddress] = 1 + rawtx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], target) + rawtx = self.nodes[0].fundrawtransaction(rawtx)['hex'] + rawtx = self.nodes[0].signrawtransaction(rawtx)['hex'] + new_txid = self.nodes[0].sendrawtransaction(rawtx) + + if with_dummy_input_output: + decoded = self.nodes[0].decoderawtransaction(rawtx) + for i in range(len(decoded['vout'])): + # make sure this one can only be spent when explicitely creating a rawtx with these outputs as inputs + # this ensures that no other TX is chaining on top of this TX + lock = self.nodes[0].lockunspent(False, [{'txid': new_txid, 'vout': i}]) + for txin in decoded['vin']: + if txin['txid'] != txid or txin['vout'] != vout: + return txin + return None + + def mine_block(self, node, vtx=[], miner_address=None, mn_payee=None, mn_amount=None, use_mnmerkleroot_from_tip=False, expected_error=None): + bt = node.getblocktemplate() + height = bt['height'] + tip_hash = bt['previousblockhash'] + + tip_block = node.getblock(tip_hash) + + coinbasevalue = bt['coinbasevalue'] + if miner_address is None: + miner_address = node.getnewaddress() + if mn_payee is None: + if isinstance(bt['masternode'], list): + mn_payee = bt['masternode'][0]['payee'] + else: + mn_payee = bt['masternode']['payee'] + # we can't take the masternode payee amount from the template here as we might have additional fees in vtx + + # calculate fees that the block template included (we'll have to remove it from the coinbase as we won't + # include the template's transactions + bt_fees = 0 + for tx in bt['transactions']: + bt_fees += tx['fee'] + + new_fees = 0 + for tx in vtx: + in_value = 0 + out_value = 0 + for txin in tx.vin: + txout = node.gettxout("%064x" % txin.prevout.hash, txin.prevout.n, False) + in_value += int(txout['value'] * COIN) + for txout in tx.vout: + out_value += txout.nValue + new_fees += in_value - out_value + + # fix fees + coinbasevalue -= bt_fees + coinbasevalue += new_fees + + if mn_amount is None: + mn_amount = get_masternode_payment(height, coinbasevalue) + miner_amount = coinbasevalue - mn_amount + + outputs = {miner_address: str(Decimal(miner_amount) / COIN)} + if mn_amount > 0: + outputs[mn_payee] = str(Decimal(mn_amount) / COIN) + + coinbase = FromHex(CTransaction(), node.createrawtransaction([], outputs)) + coinbase.vin = create_coinbase(height).vin + + # We can't really use this one as it would result in invalid merkle roots for masternode lists + if len(bt['coinbase_payload']) != 0: + cbtx = FromHex(CCbTx(version=1), bt['coinbase_payload']) + if use_mnmerkleroot_from_tip: + if 'cbTx' in tip_block: + cbtx.merkleRootMNList = int(tip_block['cbTx']['merkleRootMNList'], 16) + else: + cbtx.merkleRootMNList = 0 + coinbase.nVersion = 3 + coinbase.nType = 5 # CbTx + coinbase.vExtraPayload = cbtx.serialize() + + coinbase.calc_sha256() + + block = create_block(int(tip_hash, 16), coinbase) + block.vtx += vtx + + # Add quorum commitments from template + for tx in bt['transactions']: + tx2 = FromHex(CTransaction(), tx['data']) + if tx2.nType == 6: + block.vtx.append(tx2) + + block.hashMerkleRoot = block.calc_merkle_root() + block.solve() + result = node.submitblock(ToHex(block)) + if expected_error is not None and result != expected_error: + raise AssertionError('mining the block should have failed with error %s, but submitblock returned %s' % (expected_error, result)) + elif expected_error is None and result is not None: + raise AssertionError('submitblock returned %s' % (result)) + + def mine_double_spend(self, node, txins, target_address, use_mnmerkleroot_from_tip=False): + amount = Decimal(0) + for txin in txins: + txout = node.gettxout(txin['txid'], txin['vout'], False) + amount += txout['value'] + amount -= Decimal("0.001") # fee + + rawtx = node.createrawtransaction(txins, {target_address: amount}) + rawtx = node.signrawtransaction(rawtx)['hex'] + tx = FromHex(CTransaction(), rawtx) + + self.mine_block(node, [tx], use_mnmerkleroot_from_tip=use_mnmerkleroot_from_tip) + + def test_invalid_mn_payment(self, node): + mn_payee = self.nodes[0].getnewaddress() + self.mine_block(node, mn_payee=mn_payee, expected_error='bad-cb-payee') + self.mine_block(node, mn_amount=1, expected_error='bad-cb-payee') + +if __name__ == '__main__': + AIP3Test().main() diff --git a/qa/rpc-tests/autoix-mempool.py b/qa/rpc-tests/autoix-mempool.py new file mode 100644 index 000000000000..6c6443ff9632 --- /dev/null +++ b/qa/rpc-tests/autoix-mempool.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.mininode import * +from test_framework.test_framework import DashTestFramework +from test_framework.util import * +from time import * + +''' +autoix-mempool.py + +Checks if automatic InstantSend locks stop working when transaction mempool +is full (more than 0.1 part from max value). + +''' + +MAX_MEMPOOL_SIZE = 5 # max node mempool in MBs +MB_SIZE = 1000000 # C++ code use this coefficient to calc MB in mempool +AUTO_IX_MEM_THRESHOLD = 0.1 + + +class AutoIXMempoolTest(DashTestFramework): + def __init__(self): + super().__init__(13, 10, ["-maxmempool=%d" % MAX_MEMPOOL_SIZE]) + # set sender, receiver + self.receiver_idx = self.num_nodes - 2 + self.sender_idx = self.num_nodes - 3 + + def get_autoix_bip9_status(self): + info = self.nodes[0].getblockchaininfo() + # we reuse the aip3 deployment + return info['bip9_softforks']['aip0003']['status'] + + def activate_autoix_bip9(self): + # sync nodes periodically + # if we sync them too often, activation takes too many time + # if we sync them too rarely, nodes failed to update its state and + # bip9 status is not updated + # so, in this code nodes are synced once per 20 blocks + counter = 0 + sync_period = 10 + + while self.get_autoix_bip9_status() == 'defined': + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + counter += 1 + if counter % sync_period == 0: + # sync nodes + self.sync_all() + + while self.get_autoix_bip9_status() == 'started': + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + counter += 1 + if counter % sync_period == 0: + # sync nodes + self.sync_all() + + while self.get_autoix_bip9_status() == 'locked_in': + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + counter += 1 + if counter % sync_period == 0: + # sync nodes + self.sync_all() + + # sync nodes + self.sync_all() + + assert(self.get_autoix_bip9_status() == 'active') + + def get_autoix_spork_state(self): + info = self.nodes[0].spork('active') + return info['SPORK_16_INSTANTSEND_AUTOLOCKS'] + + def set_autoix_spork_state(self, state): + if state: + value = 0 + else: + value = 4070908800 + self.nodes[0].spork('SPORK_16_INSTANTSEND_AUTOLOCKS', value) + + # sends regular IX with high fee and may inputs (not-simple transaction) + def send_regular_IX(self, sender, receiver): + receiver_addr = receiver.getnewaddress() + txid = sender.instantsendtoaddress(receiver_addr, 1.0) + return self.wait_for_instantlock(txid, sender) + + # sends simple trx, it should become IX if autolocks are allowed + def send_simple_tx(self, sender, receiver): + raw_tx = self.create_raw_trx(sender, receiver, 1.0, 1, 4) + txid = self.nodes[0].sendrawtransaction(raw_tx['hex']) + self.sync_all() + return self.wait_for_instantlock(txid, sender) + + def get_mempool_usage(self, node): + info = node.getmempoolinfo() + return info['usage'] + + def fill_mempool(self): + # send lots of txes to yourself just to fill the mempool + counter = 0 + sync_period = 10 + dummy_address = self.nodes[0].getnewaddress() + while self.get_mempool_usage(self.nodes[self.sender_idx]) < MAX_MEMPOOL_SIZE * MB_SIZE * AUTO_IX_MEM_THRESHOLD: + self.nodes[0].sendtoaddress(dummy_address, 1.0) + counter += 1 + if counter % sync_period == 0: + # sync nodes + self.sync_all() + self.sync_all() + + def run_test(self): + # make sure masternodes are synced + sync_masternodes(self.nodes) + self.enforce_masternode_payments() # required for bip9 activation + self.activate_autoix_bip9() + self.set_autoix_spork_state(True) + + # check pre-conditions for autoIX + assert(self.get_autoix_bip9_status() == 'active') + assert(self.get_autoix_spork_state()) + + # create 3 inputs for txes on sender node and give them 6 confirmations + sender = self.nodes[self.sender_idx] + receiver = self.nodes[self.receiver_idx] + sender_address = sender.getnewaddress() + for i in range(0, 3): + self.nodes[0].sendtoaddress(sender_address, 2.0) + for i in range(0, 6): + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + self.sync_all() + + # autoIX is working + assert(self.send_simple_tx(sender, receiver)) + + # fill mempool with transactions + self.fill_mempool() + + # autoIX is not working now + assert(not self.send_simple_tx(sender, receiver)) + # regular IX is still working + assert(self.send_regular_IX(sender, receiver)) + + +if __name__ == '__main__': + AutoIXMempoolTest().main() diff --git a/qa/rpc-tests/invalidblockrequest.py b/qa/rpc-tests/invalidblockrequest.py index dd581d85153d..b6f1e4ffe6f0 100755 --- a/qa/rpc-tests/invalidblockrequest.py +++ b/qa/rpc-tests/invalidblockrequest.py @@ -33,7 +33,7 @@ def run_test(self): self.tip = None self.block_time = None NetworkThread().start() # Start up network handling in another thread - sync_masternodes(self.nodes) + sync_masternodes(self.nodes, True) test.run() def get_tests(self): diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index 21cd9f89d647..4bdad6919553 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -93,108 +93,5 @@ def run_test(self): {"category":"receive","amount":Decimal("0.1")}, {"txid":txid, "account" : "watchonly"} ) - # rbf is disabled in Absolute Core - # self.run_rbf_opt_in_test() - - # Check that the opt-in-rbf flag works properly, for sent and received - # transactions. - def run_rbf_opt_in_test(self): - # Check whether a transaction signals opt-in RBF itself - def is_opt_in(node, txid): - rawtx = node.getrawtransaction(txid, 1) - for x in rawtx["vin"]: - if x["sequence"] < 0xfffffffe: - return True - return False - - # Find an unconfirmed output matching a certain txid - def get_unconfirmed_utxo_entry(node, txid_to_match): - utxo = node.listunspent(0, 0) - for i in utxo: - if i["txid"] == txid_to_match: - return i - return None - - # 1. Chain a few transactions that don't opt-in. - txid_1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) - assert(not is_opt_in(self.nodes[0], txid_1)) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"}) - sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_1}, {"bip125-replaceable":"no"}) - - # Tx2 will build off txid_1, still not opting in to RBF. - utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_1) - - # Create tx2 using createrawtransaction - inputs = [{"txid":utxo_to_use["txid"], "vout":utxo_to_use["vout"]}] - outputs = {self.nodes[0].getnewaddress(): 0.999} - tx2 = self.nodes[1].createrawtransaction(inputs, outputs) - tx2_signed = self.nodes[1].signrawtransaction(tx2)["hex"] - txid_2 = self.nodes[1].sendrawtransaction(tx2_signed) - - # ...and check the result - assert(not is_opt_in(self.nodes[1], txid_2)) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"}) - sync_mempools(self.nodes) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_2}, {"bip125-replaceable":"no"}) - - # Tx3 will opt-in to RBF - utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[0], txid_2) - inputs = [{"txid": txid_2, "vout":utxo_to_use["vout"]}] - outputs = {self.nodes[1].getnewaddress(): 0.998} - tx3 = self.nodes[0].createrawtransaction(inputs, outputs) - tx3_modified = txFromHex(tx3) - tx3_modified.vin[0].nSequence = 0 - tx3 = bytes_to_hex_str(tx3_modified.serialize()) - tx3_signed = self.nodes[0].signrawtransaction(tx3)['hex'] - txid_3 = self.nodes[0].sendrawtransaction(tx3_signed) - - assert(is_opt_in(self.nodes[0], txid_3)) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"}) - sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_3}, {"bip125-replaceable":"yes"}) - - # Tx4 will chain off tx3. Doesn't signal itself, but depends on one - # that does. - utxo_to_use = get_unconfirmed_utxo_entry(self.nodes[1], txid_3) - inputs = [{"txid": txid_3, "vout":utxo_to_use["vout"]}] - outputs = {self.nodes[0].getnewaddress(): 0.997} - tx4 = self.nodes[1].createrawtransaction(inputs, outputs) - tx4_signed = self.nodes[1].signrawtransaction(tx4)["hex"] - txid_4 = self.nodes[1].sendrawtransaction(tx4_signed) - - assert(not is_opt_in(self.nodes[1], txid_4)) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"}) - sync_mempools(self.nodes) - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"yes"}) - - # Replace tx3, and check that tx4 becomes unknown - tx3_b = tx3_modified - tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee - tx3_b = bytes_to_hex_str(tx3_b.serialize()) - tx3_b_signed = self.nodes[0].signrawtransaction(tx3_b)['hex'] - txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True) - assert(is_opt_in(self.nodes[0], txid_3b)) - - assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"}) - sync_mempools(self.nodes) - assert_array_result(self.nodes[1].listtransactions(), {"txid": txid_4}, {"bip125-replaceable":"unknown"}) - - # Check gettransaction as well: - for n in self.nodes[0:2]: - assert_equal(n.gettransaction(txid_1)["bip125-replaceable"], "no") - assert_equal(n.gettransaction(txid_2)["bip125-replaceable"], "no") - assert_equal(n.gettransaction(txid_3)["bip125-replaceable"], "yes") - assert_equal(n.gettransaction(txid_3b)["bip125-replaceable"], "yes") - assert_equal(n.gettransaction(txid_4)["bip125-replaceable"], "unknown") - - # After mining a transaction, it's no longer BIP125-replaceable - self.nodes[0].generate(1) - assert(txid_3b not in self.nodes[0].getrawmempool()) - assert_equal(self.nodes[0].gettransaction(txid_3b)["bip125-replaceable"], "no") - assert_equal(self.nodes[0].gettransaction(txid_4)["bip125-replaceable"], "unknown") - - if __name__ == '__main__': ListTransactionsTest().main() - diff --git a/qa/rpc-tests/multikeysporks.py b/qa/rpc-tests/multikeysporks.py new file mode 100644 index 000000000000..8d4f8c38e670 --- /dev/null +++ b/qa/rpc-tests/multikeysporks.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from time import * + +''' +multikeysporks.py + +Test logic for several signer keys usage for spork broadcast. + +We set 5 possible keys for sporks signing and set minimum +required signers to 3. We check 1 and 2 signers can't set the spork +value, any 3 signers can change spork value and other 3 signers +can change it again. +''' + + +class MultiKeySporkTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 5 + self.setup_clean_chain = True + self.is_network_split = False + + def setup_network(self): + self.nodes = [] + + # secret(base58): 931wyuRNVYvhg18Uu9bky5Qg1z4QbxaJ7fefNBzjBPiLRqcd33F + # keyid(hex): 60f0f57f71f0081f1aacdd8432340a33a526f91b + # address(base58): yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa + + # secret(base58): 91vbXGMSWKGHom62986XtL1q2mQDA12ngcuUNNe5NfMSj44j7g3 + # keyid(hex): 43dff2b09de2f904f688ec14ee6899087b889ad0 + # address(base58): yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h + + # secret(base58): 92bxUjPT5AhgXuXJwfGGXqhomY2SdQ55MYjXyx9DZNxCABCSsRH + # keyid(hex): d9aa5fa00cce99101a4044e65dc544d1579890de + # address(base58): ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7 + + # secret(base58): 934yPXiVGf4RCY2qTs2Bt5k3TEtAiAg12sMxCt8yVWbSU7p3fuD + # keyid(hex): 0b23935ce0bea3b997a334f6fa276c9fa17687b2 + # address(base58): ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn + + # secret(base58): 92Cxwia363Wg2qGF1fE5z4GKi8u7r1nrWQXdtsj2ACZqaDPSihD + # keyid(hex): 1d1098b2b1f759b678a0a7a098637a9b898adcac + # address(base58): yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui + + self.nodes.append(start_node(0, self.options.tmpdir, + ["-debug", "-sporkkey=931wyuRNVYvhg18Uu9bky5Qg1z4QbxaJ7fefNBzjBPiLRqcd33F", + "-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7", + "-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h", + "-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa", + "-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn", + "-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui", + "-minsporkkeys=3"])) + self.nodes.append(start_node(1, self.options.tmpdir, + ["-debug", "-sporkkey=91vbXGMSWKGHom62986XtL1q2mQDA12ngcuUNNe5NfMSj44j7g3", + "-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7", + "-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h", + "-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa", + "-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn", + "-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui", + "-minsporkkeys=3"])) + self.nodes.append(start_node(2, self.options.tmpdir, + ["-debug", "-sporkkey=92bxUjPT5AhgXuXJwfGGXqhomY2SdQ55MYjXyx9DZNxCABCSsRH", + "-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7", + "-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h", + "-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa", + "-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn", + "-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui", + "-minsporkkeys=3"])) + self.nodes.append(start_node(3, self.options.tmpdir, + ["-debug", "-sporkkey=934yPXiVGf4RCY2qTs2Bt5k3TEtAiAg12sMxCt8yVWbSU7p3fuD", + "-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7", + "-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h", + "-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa", + "-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn", + "-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui", + "-minsporkkeys=3"])) + self.nodes.append(start_node(4, self.options.tmpdir, + ["-debug", "-sporkkey=92Cxwia363Wg2qGF1fE5z4GKi8u7r1nrWQXdtsj2ACZqaDPSihD", + "-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7", + "-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h", + "-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa", + "-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn", + "-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui", + "-minsporkkeys=3"])) + # connect nodes at start + for i in range(0, 5): + for j in range(i, 5): + connect_nodes(self.nodes[i], j) + + def get_test_spork_state(self, node): + info = node.spork('show') + # use InstantSend spork for tests + return info['SPORK_2_INSTANTSEND_ENABLED'] + + def set_test_spork_state(self, node, value): + # use InstantSend spork for tests + node.spork('SPORK_2_INSTANTSEND_ENABLED', value) + + def wait_for_test_spork_state(self, node, value): + start = time() + got_state = False + while True: + if self.get_test_spork_state(node) == value: + got_state = True + break + if time() > start + 10: + break + sleep(0.1) + return got_state + + def run_test(self): + # check test spork default state + for node in self.nodes: + assert(self.get_test_spork_state(node) == 0) + + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + # first and second signers set spork value + self.set_test_spork_state(self.nodes[0], 1) + self.set_test_spork_state(self.nodes[1], 1) + # spork change requires at least 3 signers + for node in self.nodes: + assert(not self.wait_for_test_spork_state(node, 1)) + + # third signer set spork value + self.set_test_spork_state(self.nodes[2], 1) + # now spork state is changed + for node in self.nodes: + assert(self.wait_for_test_spork_state(node, 1)) + + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + # now set the spork again with other signers to test + # old and new spork messages interaction + self.set_test_spork_state(self.nodes[2], 2) + self.set_test_spork_state(self.nodes[3], 2) + self.set_test_spork_state(self.nodes[4], 2) + for node in self.nodes: + assert(self.wait_for_test_spork_state(node, 2)) + + +if __name__ == '__main__': + MultiKeySporkTest().main() diff --git a/qa/rpc-tests/nulldummy.py b/qa/rpc-tests/nulldummy.py index d1e0457a4fbe..c3bd37289b9b 100755 --- a/qa/rpc-tests/nulldummy.py +++ b/qa/rpc-tests/nulldummy.py @@ -113,7 +113,8 @@ def tx_submit(self, node, tx, msg = ""): def block_submit(self, node, txs, accept = False): - block = create_block(self.tip, create_coinbase(self.lastblockheight + 1), self.lastblocktime + 1) + aip4_activated = self.lastblockheight + 1 >= 432 + block = create_block(self.tip, create_coinbase(self.lastblockheight + 1, aip4_activated=aip4_activated), self.lastblocktime + 1) block.nVersion = 4 for tx in txs: tx.rehash() diff --git a/qa/rpc-tests/p2p-autoinstantsend.py b/qa/rpc-tests/p2p-autoinstantsend.py new file mode 100644 index 000000000000..44335546a242 --- /dev/null +++ b/qa/rpc-tests/p2p-autoinstantsend.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.mininode import * +from test_framework.test_framework import DashTestFramework +from test_framework.util import * +from time import * + +''' +p2p-autoinstantsend.py + +Test automatic InstantSend locks functionality. + +Checks that simple transactions automatically become InstantSend locked, +complex transactions don't become IS-locked and this functionality is +activated only if it is BIP9-activated and SPORK_16_INSTANTSEND_AUTOLOCKS is +active. + +Also checks that this functionality doesn't influence regular InstantSend +transactions with high fee. +''' + +class AutoInstantSendTest(DashTestFramework): + def __init__(self): + super().__init__(14, 10, []) + # set sender, receiver, isolated nodes + self.isolated_idx = self.num_nodes - 1 + self.receiver_idx = self.num_nodes - 2 + self.sender_idx = self.num_nodes - 3 + + def get_autoix_bip9_status(self): + info = self.nodes[0].getblockchaininfo() + # we reuse the aip3 deployment + return info['bip9_softforks']['aip0003']['status'] + + def activate_autoix_bip9(self): + # sync nodes periodically + # if we sync them too often, activation takes too many time + # if we sync them too rarely, nodes failed to update its state and + # bip9 status is not updated + # so, in this code nodes are synced once per 20 blocks + counter = 0 + sync_period = 10 + + while self.get_autoix_bip9_status() == 'defined': + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + counter += 1 + if counter % sync_period == 0: + # sync nodes + self.sync_all() + + while self.get_autoix_bip9_status() == 'started': + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + counter += 1 + if counter % sync_period == 0: + # sync nodes + self.sync_all() + + while self.get_autoix_bip9_status() == 'locked_in': + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + counter += 1 + if counter % sync_period == 0: + # sync nodes + self.sync_all() + + # sync nodes + self.sync_all() + + assert(self.get_autoix_bip9_status() == 'active') + + def get_autoix_spork_state(self): + info = self.nodes[0].spork('active') + return info['SPORK_16_INSTANTSEND_AUTOLOCKS'] + + def set_autoix_spork_state(self, state): + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + if state: + value = 0 + else: + value = 4070908800 + self.nodes[0].spork('SPORK_16_INSTANTSEND_AUTOLOCKS', value) + + # sends regular IX with high fee and may inputs (not-simple transaction) + def send_regular_IX(self): + receiver_addr = self.nodes[self.receiver_idx].getnewaddress() + txid = self.nodes[0].instantsendtoaddress(receiver_addr, 1.0) + MIN_FEE = satoshi_round(-0.0001) + fee = self.nodes[0].gettransaction(txid)['fee'] + expected_fee = MIN_FEE * len(self.nodes[0].getrawtransaction(txid, True)['vin']) + assert_equal(fee, expected_fee) + return self.wait_for_instantlock(txid, self.nodes[0]) + + # sends simple trx, it should become IX if autolocks are allowed + def send_simple_tx(self): + raw_tx = self.create_raw_trx(self.nodes[0], self.nodes[self.receiver_idx], 1.0, 1, 4) + txid = self.nodes[0].sendrawtransaction(raw_tx['hex']) + self.sync_all() + return self.wait_for_instantlock(txid, self.nodes[0]) + + # sends complex trx, it should never become IX + def send_complex_tx(self): + raw_tx = self.create_raw_trx(self.nodes[0], self.nodes[self.receiver_idx], 1.0, 5, 100) + txid = self.nodes[0].sendrawtransaction(raw_tx['hex']) + self.sync_all() + return self.wait_for_instantlock(txid, self.nodes[0]) + + def run_test(self): + # make sure masternodes are synced + sync_masternodes(self.nodes) + # feed the sender with some balance + sender_addr = self.nodes[self.sender_idx].getnewaddress() + self.nodes[0].sendtoaddress(sender_addr, 1) + # make sender funds mature for InstantSend + for i in range(0, 2): + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + + self.enforce_masternode_payments() # required for bip9 activation + assert(self.get_autoix_bip9_status() == 'defined') + assert(not self.get_autoix_spork_state()) + + assert(self.send_regular_IX()) + assert(not self.send_simple_tx()) + assert(not self.send_complex_tx()) + + self.activate_autoix_bip9() + self.set_autoix_spork_state(True) + + assert(self.get_autoix_bip9_status() == 'active') + assert(self.get_autoix_spork_state()) + + assert(self.send_regular_IX()) + assert(self.send_simple_tx()) + assert(not self.send_complex_tx()) + + self.set_autoix_spork_state(False) + assert(not self.get_autoix_spork_state()) + + assert(self.send_regular_IX()) + assert(not self.send_simple_tx()) + assert(not self.send_complex_tx()) + + +if __name__ == '__main__': + AutoInstantSendTest().main() diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py index 93cd2f8b1dbf..dba440f5a003 100755 --- a/qa/rpc-tests/p2p-fullblocktest.py +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -68,7 +68,7 @@ def run_test(self): self.test = TestManager(self, self.options.tmpdir) self.test.add_all_connections(self.nodes) NetworkThread().start() # Start up network handling in another thread - sync_masternodes(self.nodes) + sync_masternodes(self.nodes, True) self.test.run() def add_transactions_to_block(self, block, tx_list): diff --git a/qa/rpc-tests/p2p-instantsend.py b/qa/rpc-tests/p2p-instantsend.py new file mode 100644 index 000000000000..69789b33f659 --- /dev/null +++ b/qa/rpc-tests/p2p-instantsend.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.mininode import * +from test_framework.test_framework import DashTestFramework +from test_framework.util import * +from time import * + +''' +InstantSendTest -- test InstantSend functionality (prevent doublespend for unconfirmed transactions) +''' + +class InstantSendTest(DashTestFramework): + def __init__(self): + super().__init__(14, 10, []) + # set sender, receiver, isolated nodes + self.isolated_idx = self.num_nodes - 1 + self.receiver_idx = self.num_nodes - 2 + self.sender_idx = self.num_nodes - 3 + + def run_test(self): + # feed the sender with some balance + sender_addr = self.nodes[self.sender_idx].getnewaddress() + self.nodes[0].sendtoaddress(sender_addr, 1) + # make sender funds mature for InstantSend + for i in range(0, 2): + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[0].generate(1) + self.sync_all() + + # create doublepending transaction, but don't relay it + dblspnd_tx = self.create_raw_trx(self.nodes[self.sender_idx], + self.nodes[self.isolated_idx], + 0.5, 1, 100) + # stop one node to isolate it from network + stop_node(self.nodes[self.isolated_idx], self.isolated_idx) + # instantsend to receiver + receiver_addr = self.nodes[self.receiver_idx].getnewaddress() + is_id = self.nodes[self.sender_idx].instantsendtoaddress(receiver_addr, 0.9) + # wait for instantsend locks + start = time() + locked = False + while True: + is_trx = self.nodes[self.sender_idx].gettransaction(is_id) + if is_trx['instantlock']: + locked = True + break + if time() > start + 10: + break + sleep(0.1) + assert(locked) + # start last node + self.nodes[self.isolated_idx] = start_node(self.isolated_idx, + self.options.tmpdir, + ["-debug"]) + # send doublespend transaction to isolated node + self.nodes[self.isolated_idx].sendrawtransaction(dblspnd_tx['hex']) + # generate block on isolated node with doublespend transaction + set_mocktime(get_mocktime() + 1) + set_node_times(self.nodes, get_mocktime()) + self.nodes[self.isolated_idx].generate(1) + wrong_block = self.nodes[self.isolated_idx].getbestblockhash() + # connect isolated block to network + for i in range(0, self.isolated_idx): + connect_nodes(self.nodes[i], self.isolated_idx) + # check doublespend block is rejected by other nodes + timeout = 10 + for i in range(0, self.isolated_idx): + res = self.nodes[i].waitforblock(wrong_block, timeout) + assert (res['hash'] != wrong_block) + # wait for long time only for first node + timeout = 1 + + +if __name__ == '__main__': + InstantSendTest().main() diff --git a/qa/rpc-tests/sporks.py b/qa/rpc-tests/sporks.py new file mode 100644 index 000000000000..f556468df08e --- /dev/null +++ b/qa/rpc-tests/sporks.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +from time import * + +''' +''' + +class SporkTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 3 + self.setup_clean_chain = True + self.is_network_split = False + + def setup_network(self): + disable_mocktime() + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, + ["-debug", "-sporkkey=cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"])) + self.nodes.append(start_node(1, self.options.tmpdir, + ["-debug"])) + self.nodes.append(start_node(2, self.options.tmpdir, + ["-debug"])) + # connect only 2 first nodes at start + connect_nodes(self.nodes[0], 1) + + def get_test_spork_state(self, node): + info = node.spork('active') + # use InstantSend spork for tests + return info['SPORK_2_INSTANTSEND_ENABLED'] + + def set_test_spork_state(self, node, state): + if state: + value = 0 + else: + value = 4070908800 + # use InstantSend spork for tests + node.spork('SPORK_2_INSTANTSEND_ENABLED', value) + + def run_test(self): + # check test spork default state + assert(self.get_test_spork_state(self.nodes[0])) + assert(self.get_test_spork_state(self.nodes[1])) + assert(self.get_test_spork_state(self.nodes[2])) + + # check spork propagation for connected nodes + self.set_test_spork_state(self.nodes[0], False) + start = time() + sent = False + while True: + if not self.get_test_spork_state(self.nodes[1]): + sent = True + break + if time() > start + 10: + break + sleep(0.1) + assert(sent) + + # restart nodes to check spork persistence + stop_node(self.nodes[0], 0) + stop_node(self.nodes[1], 1) + self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug"]) + self.nodes[1] = start_node(1, self.options.tmpdir, ["-debug"]) + assert(not self.get_test_spork_state(self.nodes[0])) + assert(not self.get_test_spork_state(self.nodes[1])) + + # Force finish mnsync node as otherwise it will never send out headers to other peers + wait_to_sync(self.nodes[1], fast_mnsync=True) + + # Generate one block to kick off masternode sync, which also starts sporks syncing for node2 + self.nodes[1].generate(1) + + # connect new node and check spork propagation after restoring from cache + connect_nodes(self.nodes[1], 2) + start = time() + sent = False + while True: + if not self.get_test_spork_state(self.nodes[2]): + sent = True + break + if time() > start + 10: + break + sleep(0.1) + assert(sent) + + +if __name__ == '__main__': + SporkTest().main() diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index 61e3f129a8a3..1f375afbafa7 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -40,9 +40,9 @@ def serialize_script_num(value): # Create a coinbase transaction, assuming no miner fees. # If pubkey is passed in, the coinbase output will be a P2PK output; # otherwise an anyone-can-spend output. -def create_coinbase(height, pubkey = None): +def create_coinbase(height, pubkey = None, aip4_activated=False): coinbase = CTransaction() - coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), + coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), ser_string(serialize_script_num(height)), 0xffffffff)) coinbaseoutput = CTxOut() coinbaseoutput.nValue = 500 * COIN @@ -53,6 +53,11 @@ def create_coinbase(height, pubkey = None): else: coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) coinbase.vout = [ coinbaseoutput ] + if aip4_activated: + coinbase.nVersion = 3 + coinbase.nType = 5 + cbtx_payload = CCbTx(1, height, 0) + coinbase.vExtraPayload = cbtx_payload.serialize() coinbase.calc_sha256() return coinbase @@ -79,3 +84,31 @@ def get_legacy_sigopcount_tx(tx, fAccurate=True): # scriptSig might be of type bytes, so convert to CScript for the moment count += CScript(j.scriptSig).GetSigOpCount(fAccurate) return count + +# Identical to GetMasternodePayment in C++ code +def get_masternode_payment(nHeight, blockValue): + ret = int(blockValue / 5) + + nMNPIBlock = 350 + nMNPIPeriod = 10 + + if nHeight > nMNPIBlock: + ret += int(blockValue / 20) + if nHeight > nMNPIBlock+(nMNPIPeriod* 1): + ret += int(blockValue / 20) + if nHeight > nMNPIBlock+(nMNPIPeriod* 2): + ret += int(blockValue / 20) + if nHeight > nMNPIBlock+(nMNPIPeriod* 3): + ret += int(blockValue / 40) + if nHeight > nMNPIBlock+(nMNPIPeriod* 4): + ret += int(blockValue / 40) + if nHeight > nMNPIBlock+(nMNPIPeriod* 5): + ret += int(blockValue / 40) + if nHeight > nMNPIBlock+(nMNPIPeriod* 6): + ret += int(blockValue / 40) + if nHeight > nMNPIBlock+(nMNPIPeriod* 7): + ret += int(blockValue / 40) + if nHeight > nMNPIBlock+(nMNPIPeriod* 9): + ret += int(blockValue / 40) + + return ret diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index 5a96602acad8..4e2b596b6783 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -213,7 +213,7 @@ def blocks_requested(): ) # --> error if not requested - if not wait_until(blocks_requested, attempts=20*num_blocks): + if not wait_until(blocks_requested, attempts=20*num_blocks, sleep=0.1): # print [ c.cb.block_request_map for c in self.connections ] raise AssertionError("Not all nodes requested block") diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 2458b856f75d..ce7f9cc694ad 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -40,7 +40,7 @@ import absolute_hash BIP0031_VERSION = 60000 -MY_VERSION = 70209 # SHORT_IDS_BLOCKS_VERSION to support cmpct blocks +MY_VERSION = 70211 # MIN_PEER_PROTO_VERSION MY_SUBVERSION = b"/python-mininode-tester:0.0.3/" MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version messages (BIP37) @@ -357,33 +357,44 @@ class CTransaction(object): def __init__(self, tx=None): if tx is None: self.nVersion = 1 + self.nType = 0 self.vin = [] self.vout = [] self.nLockTime = 0 + self.vExtraPayload = None self.sha256 = None self.hash = None else: self.nVersion = tx.nVersion + self.nType = tx.nType self.vin = copy.deepcopy(tx.vin) self.vout = copy.deepcopy(tx.vout) self.nLockTime = tx.nLockTime + self.vExtraPayload = tx.vExtraPayload self.sha256 = tx.sha256 self.hash = tx.hash def deserialize(self, f): - self.nVersion = struct.unpack("> 16) & 0xffff self.vin = deser_vector(f, CTxIn) self.vout = deser_vector(f, CTxOut) self.nLockTime = struct.unpack(" amount: + break + elif len(inputs) < max_inputs: + input = {} + input["txid"] = tx['txid'] + input['vout'] = tx['vout'] + in_amount += float(tx['amount']) + inputs.append(input) + else: + input = {} + input["txid"] = tx['txid'] + input['vout'] = tx['vout'] + in_amount -= last_amount + in_amount += float(tx['amount']) + inputs[-1] = input + last_amount = float(tx['amount']) + + assert (len(inputs) > 0) + assert (in_amount > amount) + # fill outputs + receiver_address = node_to.getnewaddress() + change_address = node_from.getnewaddress() + fee = 0.001 + outputs = {} + outputs[receiver_address] = satoshi_round(amount) + outputs[change_address] = satoshi_round(in_amount - amount - fee) + rawtx = node_from.createrawtransaction(inputs, outputs) + return node_from.signrawtransaction(rawtx) + + def wait_for_instantlock(self, txid, node): + # wait for instantsend locks + start = time() + locked = False + while True: + is_trx = node.gettransaction(txid) + if is_trx['instantlock']: + locked = True + break + if time() > start + 10: + break + sleep(0.1) + return locked + + # Test framework for doing p2p comparison testing, which sets up some bitcoind # binaries: # 1 binary: test binary diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index c18ce742c2a5..d3f4c7d1627f 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # Copyright (c) 2014-2016 The Bitcoin Core developers -# Copyright (c) 2014-2017 The Dash Core developers +# Copyright (c) 2014-2020 The Dash Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -30,7 +30,7 @@ COVERAGE_DIR = None # The maximum number of nodes a single test can spawn -MAX_NODES = 8 +MAX_NODES = 15 # Don't assign rpc or p2p ports lower than this PORT_MIN = 11000 # The number of ports to "reserve" for p2p and rpc, each @@ -105,11 +105,15 @@ def get_mnsync_status(node): result = node.mnsync("status") return result['IsSynced'] -def wait_to_sync(node): - synced = False - while not synced: +def wait_to_sync(node, fast_mnsync=False): + while True: synced = get_mnsync_status(node) - time.sleep(0.5) + if synced: + break + time.sleep(0.2) + if fast_mnsync: + # skip mnsync states + node.mnsync("next") def p2p_port(n): assert(n <= MAX_NODES) @@ -191,9 +195,9 @@ def sync_mempools(rpc_connections, *, wait=1, timeout=60): timeout -= wait raise AssertionError("Mempool sync failed") -def sync_masternodes(rpc_connections): +def sync_masternodes(rpc_connections, fast_mnsync=False): for node in rpc_connections: - wait_to_sync(node) + wait_to_sync(node, fast_mnsync) bitcoind_processes = {} diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py index a8d28ed234ed..d8fbb0c86ca5 100755 --- a/qa/rpc-tests/walletbackup.py +++ b/qa/rpc-tests/walletbackup.py @@ -159,6 +159,7 @@ def run_test(self): # Start node2 with no chain shutil.rmtree(self.options.tmpdir + "/node2/regtest/blocks") shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate") + shutil.rmtree(self.options.tmpdir + "/node2/regtest/evodb") # Restore wallets from backup shutil.copyfile(tmpdir + "/node0/wallet.bak", tmpdir + "/node0/regtest/wallet.dat") @@ -180,6 +181,7 @@ def run_test(self): #start node2 with no chain shutil.rmtree(self.options.tmpdir + "/node2/regtest/blocks") shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate") + shutil.rmtree(self.options.tmpdir + "/node2/regtest/evodb") self.start_three() diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py index 9106af45613c..80b38fb4e355 100755 --- a/share/qt/extract_strings_qt.py +++ b/share/qt/extract_strings_qt.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (c) 2012-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -6,7 +6,6 @@ Extract _("...") strings for translation and convert to Qt stringdefs so that they can be picked up by Qt linguist. ''' -from __future__ import division,print_function,unicode_literals from subprocess import Popen, PIPE import operator import os diff --git a/share/rpcuser/rpcuser.py b/share/rpcuser/rpcuser.py index f806a810e08c..ab3d8c4ed3de 100755 --- a/share/rpcuser/rpcuser.py +++ b/share/rpcuser/rpcuser.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright (c) 2015 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/.clang-format b/src/.clang-format index fc53509138fa..1e8222754462 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -10,11 +10,13 @@ AllowShortIfStatementsOnASingleLine: true AllowShortLoopsOnASingleLine: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true +BinPackArguments: true BinPackParameters: false BreakBeforeBinaryOperators: false BreakBeforeBraces: Linux -BreakBeforeTernaryOperators: false +BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: AfterColon ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: false @@ -35,8 +37,8 @@ ObjCSpaceBeforeProtocolList: false PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 -PenaltyBreakString: 1000 -PenaltyExcessCharacter: 1000000 +PenaltyBreakString: 100 +PenaltyExcessCharacter: 5 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left SpaceBeforeAssignmentOperators: true diff --git a/src/Makefile.am b/src/Makefile.am index 382b71d160cb..edd9e412dbda 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,7 @@ BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAG BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS) +BLS_LIBS=-lchiabls -lgmp LIBBITCOIN_SERVER=libabsolute_server.a LIBBITCOIN_COMMON=libabsolute_common.a LIBBITCOIN_CONSENSUS=libabsolute_consensus.a @@ -105,6 +106,13 @@ BITCOIN_CORE_H = \ core_io.h \ core_memusage.h \ cuckoocache.h \ + ctpl.h \ + evo/evodb.h \ + evo/specialtx.h \ + evo/providertx.h \ + evo/deterministicmns.h \ + evo/cbtx.h \ + evo/simplifiedmns.h \ privatesend.h \ privatesend-client.h \ privatesend-server.h \ @@ -147,7 +155,6 @@ BITCOIN_CORE_H = \ noui.h \ policy/fees.h \ policy/policy.h \ - policy/rbf.h \ pow.h \ protocol.h \ random.h \ @@ -215,6 +222,12 @@ libabsolute_server_a_SOURCES = \ chain.cpp \ checkpoints.cpp \ dsnotificationinterface.cpp \ + evo/evodb.cpp \ + evo/specialtx.cpp \ + evo/providertx.cpp \ + evo/deterministicmns.cpp \ + evo/cbtx.cpp \ + evo/simplifiedmns.cpp \ httprpc.cpp \ httpserver.cpp \ init.cpp \ @@ -251,6 +264,7 @@ libabsolute_server_a_SOURCES = \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ + rpc/rpcevo.cpp \ rpc/server.cpp \ script/sigcache.cpp \ script/ismine.cpp \ @@ -290,7 +304,6 @@ libabsolute_wallet_a_SOURCES = \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ - policy/rbf.cpp \ $(BITCOIN_CORE_H) # crypto primitives library @@ -305,7 +318,6 @@ crypto_libabsolute_crypto_a_SOURCES = \ crypto/hmac_sha512.cpp \ crypto/hmac_sha512.h \ crypto/ripemd160.cpp \ - crypto/aes_helper.c \ crypto/ripemd160.h \ crypto/sha1.cpp \ crypto/sha1.h \ @@ -313,6 +325,7 @@ crypto_libabsolute_crypto_a_SOURCES = \ crypto/sha256.h \ crypto/sha512.cpp \ crypto/sha512.h \ + crypto/Lyra2RE/aes_helper.c \ crypto/Lyra2RE/Lyra2RE.c \ crypto/Lyra2RE/Lyra2RE.h \ crypto/Lyra2RE/Lyra2.c \ @@ -320,44 +333,28 @@ crypto_libabsolute_crypto_a_SOURCES = \ crypto/Lyra2RE/Sponge.c \ crypto/Lyra2RE/Sponge.h \ crypto/Lyra2RE/sph_blake.h \ - crypto/Lyra2RE/sph_bmw.h \ - crypto/Lyra2RE/sph_cubehash.h \ crypto/Lyra2RE/sph_groestl.h \ crypto/Lyra2RE/sph_keccak.h \ crypto/Lyra2RE/sph_skein.h \ crypto/Lyra2RE/sph_types.h \ + crypto/Lyra2RE/sph_cubehash.h \ crypto/Lyra2RE/blake.c \ - crypto/Lyra2RE/groestl.c \ crypto/Lyra2RE/keccak.c \ crypto/Lyra2RE/skein.c \ + crypto/Lyra2RE/bmw.c \ crypto/Lyra2RE/cubehash.c \ - crypto/Lyra2RE/bmw.c - -# x11 -crypto_libabsolute_crypto_a_SOURCES += \ - crypto/blake.c \ - crypto/bmw.c \ - crypto/cubehash.c \ - crypto/echo.c \ - crypto/groestl.c \ - crypto/jh.c \ - crypto/keccak.c \ - crypto/luffa.c \ - crypto/shavite.c \ - crypto/simd.c \ - crypto/skein.c \ - crypto/sph_blake.h \ - crypto/sph_bmw.h \ - crypto/sph_cubehash.h \ - crypto/sph_echo.h \ - crypto/sph_groestl.h \ - crypto/sph_jh.h \ - crypto/sph_keccak.h \ - crypto/sph_luffa.h \ - crypto/sph_shavite.h \ - crypto/sph_simd.h \ - crypto/sph_skein.h \ - crypto/sph_types.h + crypto/Lyra2RE/echo.c \ + crypto/Lyra2RE/groestl.c \ + crypto/Lyra2RE/jh.c \ + crypto/Lyra2RE/luffa.c \ + crypto/Lyra2RE/shavite.c \ + crypto/Lyra2RE/simd.c \ + crypto/Lyra2RE/sph_bmw.h \ + crypto/Lyra2RE/sph_echo.h \ + crypto/Lyra2RE/sph_jh.h \ + crypto/Lyra2RE/sph_luffa.h \ + crypto/Lyra2RE/sph_shavite.h \ + crypto/Lyra2RE/sph_simd.h # consensus: shared between all executables that validate any consensus rules. libabsolute_consensus_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) @@ -366,6 +363,8 @@ libabsolute_consensus_a_SOURCES = \ amount.h \ arith_uint256.cpp \ arith_uint256.h \ + bls/bls.cpp \ + bls/bls.h \ consensus/merkle.cpp \ consensus/merkle.h \ consensus/params.h \ @@ -424,6 +423,10 @@ libabsolute_common_a_SOURCES = \ libabsolute_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libabsolute_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libabsolute_util_a_SOURCES = \ + bls/bls_ies.cpp \ + bls/bls_ies.h \ + bls/bls_worker.cpp \ + bls/bls_worker.h \ support/lockedpool.cpp \ chainparamsbase.cpp \ clientversion.cpp \ @@ -443,6 +446,7 @@ libabsolute_util_a_SOURCES = \ if GLIBC_BACK_COMPAT libabsolute_util_a_SOURCES += compat/glibc_compat.cpp +AM_LDFLAGS += $(COMPAT_LDFLAGS) endif # cli: shared between absolute-cli and absolute-qt @@ -478,7 +482,7 @@ absoluted_LDADD = \ $(LIBMEMENV) \ $(LIBSECP256K1) -absoluted_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) +absoluted_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(BLS_LIBS) # absolute-cli binary # absolute_cli_SOURCES = absolute-cli.cpp @@ -495,7 +499,7 @@ absolute_cli_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) -absolute_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) +absolute_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) $(BLS_LIBS) # # absolute-tx binary # @@ -516,7 +520,7 @@ absolute_tx_LDADD = \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) -absolute_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) +absolute_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(BLS_LIBS) # # absoluteconsensus library # @@ -542,7 +546,7 @@ if GLIBC_BACK_COMPAT endif libabsoluteconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) -libabsoluteconsensus_la_LIBADD = $(LIBSECP256K1) +libabsoluteconsensus_la_LIBADD = $(LIBSECP256K1) $(BLS_LIBS) libabsoluteconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL libabsoluteconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -569,9 +573,12 @@ CLEANFILES += wallet/*.gcda wallet/*.gcno CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno CLEANFILES += zmq/*.gcda zmq/*.gcno +IMMER_DIST = immer + DISTCLEANFILES = obj/build.h EXTRA_DIST = $(CTAES_DIST) +EXTRA_DIST += $(IMMER_DIST) config/absolute-config.h: config/stamp-h1 diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 9fecddf0fda4..da5a333ce942 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -14,8 +14,11 @@ bench_bench_absolute_SOURCES = \ bench/bench_absolute.cpp \ bench/bench.cpp \ bench/bench.h \ + bench/bls.cpp \ + bench/bls_dkg.cpp \ bench/checkblock.cpp \ bench/checkqueue.cpp \ + bench/ecdsa.cpp \ bench/Examples.cpp \ bench/rollingbloom.cpp \ bench/crypto_hash.cpp \ @@ -51,7 +54,7 @@ bench_bench_absolute_SOURCES += bench/coin_selection.cpp bench_bench_absolute_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO) endif -bench_bench_absolute_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) +bench_bench_absolute_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(BLS_LIBS) bench_bench_absolute_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_TEST_FILES) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index c2ae6711751f..17e5c8ffdd91 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -29,7 +29,6 @@ QT_FORMS_UI = \ qt/forms/addressbookpage.ui \ qt/forms/askpassphrasedialog.ui \ qt/forms/coincontroldialog.ui \ - qt/forms/darksendconfig.ui \ qt/forms/editaddressdialog.ui \ qt/forms/helpmessagedialog.ui \ qt/forms/intro.ui \ @@ -60,7 +59,6 @@ QT_MOC_CPP = \ qt/moc_coincontroldialog.cpp \ qt/moc_coincontroltreewidget.cpp \ qt/moc_csvmodelwriter.cpp \ - qt/moc_darksendconfig.cpp \ qt/moc_editaddressdialog.cpp \ qt/moc_guiutil.cpp \ qt/moc_intro.cpp \ @@ -130,7 +128,6 @@ BITCOIN_QT_H = \ qt/coincontroldialog.h \ qt/coincontroltreewidget.h \ qt/csvmodelwriter.h \ - qt/darksendconfig.h \ qt/editaddressdialog.h \ qt/guiconstants.h \ qt/guiutil.h \ @@ -272,7 +269,6 @@ BITCOIN_QT_WALLET_CPP = \ qt/askpassphrasedialog.cpp \ qt/coincontroldialog.cpp \ qt/coincontroltreewidget.cpp \ - qt/darksendconfig.cpp \ qt/editaddressdialog.cpp \ qt/masternodelist.cpp \ qt/openuridialog.cpp \ @@ -374,7 +370,7 @@ qt_absolute_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif qt_absolute_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ - $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) + $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(BLS_LIBS) qt_absolute_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_absolute_qt_LIBTOOLFLAGS = --tag CXX diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 8af25fcc9a7e..027f7f22c938 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -50,7 +50,7 @@ endif qt_test_test_absolute_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \ $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ - $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) + $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(BLS_LIBS) qt_test_test_absolute_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_test_test_absolute_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 89a5758a4656..faa8f4565b6c 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -88,6 +88,8 @@ BITCOIN_TESTS =\ test/crypto_tests.cpp \ test/cuckoocache_tests.cpp \ test/DoS_tests.cpp \ + test/evo_deterministicmns_tests.cpp \ + test/evo_simplifiedmns_tests.cpp \ test/getarg_tests.cpp \ test/governance_validators_tests.cpp \ test/hash_tests.cpp \ @@ -153,7 +155,7 @@ if ENABLE_WALLET test_test_absolute_LDADD += $(LIBBITCOIN_WALLET) endif -test_test_absolute_LDADD += $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) +test_test_absolute_LDADD += $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(BLS_LIBS) test_test_absolute_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static if ENABLE_ZMQ diff --git a/src/absolute-cli.cpp b/src/absolute-cli.cpp index c9d3fc9aad44..8ff423027fbc 100644 --- a/src/absolute-cli.cpp +++ b/src/absolute-cli.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/absoluted.cpp b/src/absoluted.cpp index 1d4ae01d2dbe..02935b19e851 100644 --- a/src/absoluted.cpp +++ b/src/absoluted.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index c16d2ee1cba3..3221cf2ab2c2 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,21 +9,153 @@ #include "masternodeman.h" #include "netbase.h" #include "protocol.h" +#include "netbase.h" +#include "warnings.h" +#include "init.h" +#include "evo/deterministicmns.h" // Keep track of the active Masternode -CActiveMasternode activeMasternode; +CActiveMasternodeInfo activeMasternodeInfo; +CActiveLegacyMasternodeManager legacyActiveMasternodeManager; +CActiveDeterministicMasternodeManager* activeMasternodeManager; -void CActiveMasternode::ManageState(CConnman& connman) +std::string CActiveDeterministicMasternodeManager::GetStateString() const { - LogPrint("masternode", "CActiveMasternode::ManageState -- Start\n"); - if(!fMasternodeMode) { - LogPrint("masternode", "CActiveMasternode::ManageState -- Not a masternode, returning\n"); + switch (state) { + case MASTERNODE_WAITING_FOR_PROTX: return "WAITING_FOR_PROTX"; + case MASTERNODE_POSE_BANNED: return "POSE_BANNED"; + case MASTERNODE_REMOVED: return "REMOVED"; + case MASTERNODE_READY: return "READY"; + case MASTERNODE_ERROR: return "ERROR"; + default: return "UNKNOWN"; + } +} + +std::string CActiveDeterministicMasternodeManager::GetStatus() const +{ + switch (state) { + case MASTERNODE_WAITING_FOR_PROTX: return "Waiting for ProTx to appear on-chain"; + case MASTERNODE_POSE_BANNED: return "Masternode was PoSe banned"; + case MASTERNODE_REMOVED: return "Masternode removed from list"; + case MASTERNODE_READY: return "Ready"; + case MASTERNODE_ERROR: return "Error. " + strError; + default: return "Unknown"; + } +} + +void CActiveDeterministicMasternodeManager::Init() +{ + LOCK(cs_main); + + if (!fMasternodeMode) + return; + + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + + if (!GetLocalAddress(activeMasternodeInfo.service)) { + state = MASTERNODE_ERROR; + return; + } + + CDeterministicMNList mnList = deterministicMNManager->GetListAtChainTip(); + + CDeterministicMNCPtr dmn = mnList.GetMNByOperatorKey(*activeMasternodeInfo.blsPubKeyOperator); + if (!dmn) { + // MN not appeared on the chain yet + return; + } + + if (!mnList.IsMNValid(dmn->proTxHash)) { + if (mnList.IsMNPoSeBanned(dmn->proTxHash)) { + state = MASTERNODE_POSE_BANNED; + } else { + state = MASTERNODE_REMOVED; + } + return; + } + + mnListEntry = dmn; + + LogPrintf("CActiveDeterministicMasternodeManager::Init -- proTxHash=%s, proTx=%s\n", mnListEntry->proTxHash.ToString(), mnListEntry->ToString()); + + if (activeMasternodeInfo.service != mnListEntry->pdmnState->addr) { + state = MASTERNODE_ERROR; + strError = "Local address does not match the address from ProTx"; + LogPrintf("CActiveDeterministicMasternodeManager::Init -- ERROR: %s", strError); + return; + } + + if (mnListEntry->pdmnState->nProtocolVersion != PROTOCOL_VERSION) { + state = MASTERNODE_ERROR; + strError = "Local protocol version does not match version from ProTx. You may need to update the ProTx"; + LogPrintf("CActiveDeterministicMasternodeManager::Init -- ERROR: %s", strError); + return; + } + + activeMasternodeInfo.outpoint = COutPoint(mnListEntry->proTxHash, mnListEntry->nCollateralIndex); + state = MASTERNODE_READY; +} + +void CActiveDeterministicMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) +{ + LOCK(cs_main); + + if (!fMasternodeMode) return; + + if (!deterministicMNManager->IsDeterministicMNsSporkActive(pindexNew->nHeight)) { + return; + } + + if (state == MASTERNODE_WAITING_FOR_PROTX) { + Init(); + } else if (state == MASTERNODE_READY) { + if (!deterministicMNManager->HasValidMNAtBlock(pindexNew->GetBlockHash(), mnListEntry->proTxHash)) { + // MN disappeared from MN list + state = MASTERNODE_REMOVED; + activeMasternodeInfo.outpoint.SetNull(); + // MN might have reappeared in same block with a new ProTx (with same masternode key) + Init(); + } + } else if (state == MASTERNODE_REMOVED || state == MASTERNODE_POSE_BANNED) { + // MN might have reappeared with a new ProTx (with same masternode key) + Init(); } +} + +bool CActiveDeterministicMasternodeManager::GetLocalAddress(CService &addrRet) +{ + // First try to find whatever local address is specified by externalip option + bool fFoundLocal = GetLocal(addrRet) && CMasternode::IsValidNetAddr(addrRet); + if (!fFoundLocal && Params().NetworkIDString() == CBaseChainParams::REGTEST) { + if (Lookup("127.0.0.1", addrRet, GetListenPort(), false)) { + fFoundLocal = true; + } + } + if(!fFoundLocal) { + strError = "Can't detect valid external address. Please consider using the externalip configuration option if problem persists. Make sure to use IPv4 address only."; + LogPrintf("CActiveDeterministicMasternodeManager::GetLocalAddress -- ERROR: %s\n", strError); + return false; + } + return true; +} + +/********* LEGACY *********/ + +void CActiveLegacyMasternodeManager::ManageState(CConnman& connman) +{ + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + LogPrint("masternode", "CActiveLegacyMasternodeManager::ManageState -- Start\n"); + if(!fMasternodeMode) { + LogPrint("masternode", "CActiveLegacyMasternodeManager::ManageState -- Not a masternode, returning\n"); + return; + } if(Params().NetworkIDString() != CBaseChainParams::REGTEST && !masternodeSync.IsBlockchainSynced()) { nState = ACTIVE_MASTERNODE_SYNC_IN_PROCESS; - LogPrintf("CActiveMasternode::ManageState -- %s: %s\n", GetStateString(), GetStatus()); + LogPrintf("CActiveLegacyMasternodeManager::ManageState -- %s: %s\n", GetStateString(), GetStatus()); return; } @@ -31,7 +163,7 @@ void CActiveMasternode::ManageState(CConnman& connman) nState = ACTIVE_MASTERNODE_INITIAL; } - LogPrint("masternode", "CActiveMasternode::ManageState -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetTypeString(), fPingerEnabled); + LogPrint("masternode", "CActiveLegacyMasternodeManager::ManageState -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetTypeString(), fPingerEnabled); if(eType == MASTERNODE_UNKNOWN) { ManageStateInitial(connman); @@ -44,7 +176,7 @@ void CActiveMasternode::ManageState(CConnman& connman) SendMasternodePing(connman); } -std::string CActiveMasternode::GetStateString() const +std::string CActiveLegacyMasternodeManager::GetStateString() const { switch (nState) { case ACTIVE_MASTERNODE_INITIAL: return "INITIAL"; @@ -56,7 +188,7 @@ std::string CActiveMasternode::GetStateString() const } } -std::string CActiveMasternode::GetStatus() const +std::string CActiveLegacyMasternodeManager::GetStatus() const { switch (nState) { case ACTIVE_MASTERNODE_INITIAL: return "Node just started, not yet activated"; @@ -68,7 +200,7 @@ std::string CActiveMasternode::GetStatus() const } } -std::string CActiveMasternode::GetTypeString() const +std::string CActiveLegacyMasternodeManager::GetTypeString() const { std::string strType; switch(eType) { @@ -82,44 +214,47 @@ std::string CActiveMasternode::GetTypeString() const return strType; } -bool CActiveMasternode::SendMasternodePing(CConnman& connman) +bool CActiveLegacyMasternodeManager::SendMasternodePing(CConnman& connman) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return false; + if(!fPingerEnabled) { - LogPrint("masternode", "CActiveMasternode::SendMasternodePing -- %s: masternode ping service is disabled, skipping...\n", GetStateString()); + LogPrint("masternode", "CActiveLegacyMasternodeManager::SendMasternodePing -- %s: masternode ping service is disabled, skipping...\n", GetStateString()); return false; } - if(!mnodeman.Has(outpoint)) { + if(!mnodeman.Has(activeMasternodeInfo.outpoint)) { strNotCapableReason = "Masternode not in masternode list"; nState = ACTIVE_MASTERNODE_NOT_CAPABLE; - LogPrintf("CActiveMasternode::SendMasternodePing -- %s: %s\n", GetStateString(), strNotCapableReason); + LogPrintf("CActiveLegacyMasternodeManager::SendMasternodePing -- %s: %s\n", GetStateString(), strNotCapableReason); return false; } - CMasternodePing mnp(outpoint); + CMasternodePing mnp(activeMasternodeInfo.outpoint); mnp.nSentinelVersion = nSentinelVersion; mnp.fSentinelIsCurrent = (abs(GetAdjustedTime() - nSentinelPingTime) < MASTERNODE_SENTINEL_PING_MAX_SECONDS); - if(!mnp.Sign(keyMasternode, pubKeyMasternode)) { - LogPrintf("CActiveMasternode::SendMasternodePing -- ERROR: Couldn't sign Masternode Ping\n"); + if(!mnp.Sign(activeMasternodeInfo.legacyKeyOperator, activeMasternodeInfo.legacyKeyIDOperator)) { + LogPrintf("CActiveLegacyMasternodeManager::SendMasternodePing -- ERROR: Couldn't sign Masternode Ping\n"); return false; } // Update lastPing for our masternode in Masternode list - if(mnodeman.IsMasternodePingedWithin(outpoint, MASTERNODE_MIN_MNP_SECONDS, mnp.sigTime)) { - LogPrintf("CActiveMasternode::SendMasternodePing -- Too early to send Masternode Ping\n"); + if(mnodeman.IsMasternodePingedWithin(activeMasternodeInfo.outpoint, MASTERNODE_MIN_MNP_SECONDS, mnp.sigTime)) { + LogPrintf("CActiveLegacyMasternodeManager::SendMasternodePing -- Too early to send Masternode Ping\n"); return false; } - mnodeman.SetMasternodeLastPing(outpoint, mnp); + mnodeman.SetMasternodeLastPing(activeMasternodeInfo.outpoint, mnp); - LogPrintf("CActiveMasternode::SendMasternodePing -- Relaying ping, collateral=%s\n", outpoint.ToStringShort()); + LogPrintf("CActiveLegacyMasternodeManager::SendMasternodePing -- Relaying ping, collateral=%s\n", activeMasternodeInfo.outpoint.ToStringShort()); mnp.Relay(connman); return true; } -bool CActiveMasternode::UpdateSentinelPing(int version) +bool CActiveLegacyMasternodeManager::UpdateSentinelPing(int version) { nSentinelVersion = version; nSentinelPingTime = GetAdjustedTime(); @@ -127,110 +262,134 @@ bool CActiveMasternode::UpdateSentinelPing(int version) return true; } -void CActiveMasternode::ManageStateInitial(CConnman& connman) +void CActiveLegacyMasternodeManager::ManageStateInitial(CConnman& connman) { - LogPrint("masternode", "CActiveMasternode::ManageStateInitial -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetTypeString(), fPingerEnabled); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + + LogPrint("masternode", "CActiveLegacyMasternodeManager::ManageStateInitial -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetTypeString(), fPingerEnabled); // Check that our local network configuration is correct if (!fListen) { // listen option is probably overwritten by smth else, no good nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = "Masternode must accept connections from outside. Make sure listen configuration option is not overwritten by some another parameter."; - LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); return; } // First try to find whatever local address is specified by externalip option - bool fFoundLocal = GetLocal(service) && CMasternode::IsValidNetAddr(service); + bool fFoundLocal = GetLocal(activeMasternodeInfo.service) && CMasternode::IsValidNetAddr(activeMasternodeInfo.service); if(!fFoundLocal) { bool empty = true; // If we have some peers, let's try to find our local address from one of them - connman.ForEachNodeContinueIf(CConnman::AllNodes, [&fFoundLocal, &empty, this](CNode* pnode) { + connman.ForEachNodeContinueIf(CConnman::AllNodes, [&fFoundLocal, &empty](CNode* pnode) { empty = false; if (pnode->addr.IsIPv4()) - fFoundLocal = GetLocal(service, &pnode->addr) && CMasternode::IsValidNetAddr(service); + fFoundLocal = GetLocal(activeMasternodeInfo.service, &pnode->addr) && CMasternode::IsValidNetAddr(activeMasternodeInfo.service); return !fFoundLocal; }); // nothing and no live connections, can't do anything for now if (empty) { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = "Can't detect valid external address. Will retry when there are some connections available."; - LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); return; } } + if (!fFoundLocal && Params().NetworkIDString() == CBaseChainParams::REGTEST) { + if (Lookup("127.0.0.1", activeMasternodeInfo.service, GetListenPort(), false)) { + fFoundLocal = true; + } + } + if(!fFoundLocal) { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = "Can't detect valid external address. Please consider using the externalip configuration option if problem persists. Make sure to use IPv4 address only."; - LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); return; } int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); if(Params().NetworkIDString() == CBaseChainParams::MAIN) { - if(service.GetPort() != mainnetDefaultPort) { + if(activeMasternodeInfo.service.GetPort() != mainnetDefaultPort) { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; - strNotCapableReason = strprintf("Invalid port: %u - only %d is supported on mainnet.", service.GetPort(), mainnetDefaultPort); - LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + strNotCapableReason = strprintf("Invalid port: %u - only %d is supported on mainnet.", activeMasternodeInfo.service.GetPort(), mainnetDefaultPort); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); return; } - } else if(service.GetPort() == mainnetDefaultPort) { + } else if(activeMasternodeInfo.service.GetPort() == mainnetDefaultPort) { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; - strNotCapableReason = strprintf("Invalid port: %u - %d is only supported on mainnet.", service.GetPort(), mainnetDefaultPort); - LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + strNotCapableReason = strprintf("Invalid port: %u - %d is only supported on mainnet.", activeMasternodeInfo.service.GetPort(), mainnetDefaultPort); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); return; } - // Check socket connectivity - LogPrintf("CActiveMasternode::ManageStateInitial -- Checking inbound connection to '%s'\n", service.ToString()); - SOCKET hSocket; - bool fConnected = ConnectSocket(service, hSocket, nConnectTimeout) && IsSelectableSocket(hSocket); - CloseSocket(hSocket); + if(Params().NetworkIDString() != CBaseChainParams::REGTEST) { + // Check socket connectivity + LogPrintf("CActiveLegacyMasternodeManager::ManageStateInitial -- Checking inbound connection to '%s'\n", activeMasternodeInfo.service.ToString()); + SOCKET hSocket; + bool fConnected = ConnectSocket(activeMasternodeInfo.service, hSocket, nConnectTimeout) && IsSelectableSocket(hSocket); + CloseSocket(hSocket); - if (!fConnected) { - nState = ACTIVE_MASTERNODE_NOT_CAPABLE; - strNotCapableReason = "Could not connect to " + service.ToString(); - LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); - return; + if (!fConnected) { + nState = ACTIVE_MASTERNODE_NOT_CAPABLE; + strNotCapableReason = "Could not connect to " + activeMasternodeInfo.service.ToString(); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } } // Default to REMOTE eType = MASTERNODE_REMOTE; - LogPrint("masternode", "CActiveMasternode::ManageStateInitial -- End status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetTypeString(), fPingerEnabled); + LogPrint("masternode", "CActiveLegacyMasternodeManager::ManageStateInitial -- End status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetTypeString(), fPingerEnabled); } -void CActiveMasternode::ManageStateRemote() +void CActiveLegacyMasternodeManager::ManageStateRemote() { - LogPrint("masternode", "CActiveMasternode::ManageStateRemote -- Start status = %s, type = %s, pinger enabled = %d, pubKeyMasternode.GetID() = %s\n", - GetStatus(), GetTypeString(), fPingerEnabled, pubKeyMasternode.GetID().ToString()); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; - mnodeman.CheckMasternode(pubKeyMasternode, true); + LogPrint("masternode", "CActiveLegacyMasternodeManager::ManageStateRemote -- Start status = %s, type = %s, pinger enabled = %d, keyIDOperator = %s\n", + GetStatus(), GetTypeString(), fPingerEnabled, activeMasternodeInfo.legacyKeyIDOperator.ToString()); + + mnodeman.CheckMasternode(activeMasternodeInfo.legacyKeyIDOperator, true); masternode_info_t infoMn; - if(mnodeman.GetMasternodeInfo(pubKeyMasternode, infoMn)) { + if(mnodeman.GetMasternodeInfo(activeMasternodeInfo.legacyKeyIDOperator, infoMn)) { if(infoMn.nProtocolVersion != PROTOCOL_VERSION) { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = "Invalid protocol version"; - LogPrintf("CActiveMasternode::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); return; } - if(service != infoMn.addr) { + if(activeMasternodeInfo.service != infoMn.addr) { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = "Broadcasted IP doesn't match our external address. Make sure you issued a new broadcast if IP of this masternode changed recently."; - LogPrintf("CActiveMasternode::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); return; } if(!CMasternode::IsValidStateForAutoStart(infoMn.nActiveState)) { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = strprintf("Masternode in %s state", CMasternode::StateToString(infoMn.nActiveState)); - LogPrintf("CActiveMasternode::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); return; } + auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(infoMn.outpoint.hash); + if (dmn) { + if (dmn->pdmnState->addr != infoMn.addr) { + nState = ACTIVE_MASTERNODE_NOT_CAPABLE; + strNotCapableReason = strprintf("Masternode collateral is a ProTx and ProTx address does not match local address"); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + return; + } + LogPrintf("CActiveLegacyMasternodeManager::ManageStateRemote -- Collateral is a ProTx\n"); + } if(nState != ACTIVE_MASTERNODE_STARTED) { - LogPrintf("CActiveMasternode::ManageStateRemote -- STARTED!\n"); - outpoint = infoMn.outpoint; - service = infoMn.addr; + LogPrintf("CActiveLegacyMasternodeManager::ManageStateRemote -- STARTED!\n"); + activeMasternodeInfo.outpoint = infoMn.outpoint; + activeMasternodeInfo.service = infoMn.addr; fPingerEnabled = true; nState = ACTIVE_MASTERNODE_STARTED; } @@ -238,6 +397,6 @@ void CActiveMasternode::ManageStateRemote() else { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = "Masternode not in masternode list"; - LogPrintf("CActiveMasternode::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); + LogPrintf("CActiveLegacyMasternodeManager::ManageStateRemote -- %s: %s\n", GetStateString(), strNotCapableReason); } } diff --git a/src/activemasternode.h b/src/activemasternode.h index 863c892171c8..00b2b974050c 100644 --- a/src/activemasternode.h +++ b/src/activemasternode.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,8 +10,14 @@ #include "key.h" #include "net.h" #include "primitives/transaction.h" +#include "validationinterface.h" -class CActiveMasternode; +#include "evo/providertx.h" +#include "evo/deterministicmns.h" + +struct CActiveMasternodeInfo; +class CActiveLegacyMasternodeManager; +class CActiveDeterministicMasternodeManager; static const int ACTIVE_MASTERNODE_INITIAL = 0; // initial state static const int ACTIVE_MASTERNODE_SYNC_IN_PROCESS = 1; @@ -19,10 +25,56 @@ static const int ACTIVE_MASTERNODE_INPUT_TOO_NEW = 2; static const int ACTIVE_MASTERNODE_NOT_CAPABLE = 3; static const int ACTIVE_MASTERNODE_STARTED = 4; -extern CActiveMasternode activeMasternode; +extern CActiveMasternodeInfo activeMasternodeInfo; +extern CActiveLegacyMasternodeManager legacyActiveMasternodeManager; +extern CActiveDeterministicMasternodeManager* activeMasternodeManager; + +struct CActiveMasternodeInfo { + // Keys for the active Masternode + CKeyID legacyKeyIDOperator; + CKey legacyKeyOperator; + + std::unique_ptr blsPubKeyOperator; + std::unique_ptr blsKeyOperator; + + // Initialized while registering Masternode + COutPoint outpoint; + CService service; +}; -// Responsible for activating the Masternode and pinging the network -class CActiveMasternode + +class CActiveDeterministicMasternodeManager : public CValidationInterface +{ +public: + enum masternode_state_t { + MASTERNODE_WAITING_FOR_PROTX, + MASTERNODE_POSE_BANNED, + MASTERNODE_REMOVED, + MASTERNODE_READY, + MASTERNODE_ERROR, + }; + +private: + CDeterministicMNCPtr mnListEntry; + masternode_state_t state{MASTERNODE_WAITING_FOR_PROTX}; + std::string strError; + +public: + virtual void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload); + + void Init(); + + CDeterministicMNCPtr GetDMN() const { return mnListEntry; } + + std::string GetStateString() const; + std::string GetStatus() const; + +private: + bool GetLocalAddress(CService &addrRet); +}; + +// Responsible for activating the Masternode and pinging the network (legacy MN list) +class CActiveLegacyMasternodeManager { public: enum masternode_type_enum_t { @@ -46,25 +98,13 @@ class CActiveMasternode uint32_t nSentinelVersion; public: - // Keys for the active Masternode - CPubKey pubKeyMasternode; - CKey keyMasternode; - - // Initialized while registering Masternode - COutPoint outpoint; - CService service; - int nState; // should be one of ACTIVE_MASTERNODE_XXXX std::string strNotCapableReason; - CActiveMasternode() + CActiveLegacyMasternodeManager() : eType(MASTERNODE_UNKNOWN), fPingerEnabled(false), - pubKeyMasternode(), - keyMasternode(), - outpoint(), - service(), nState(ACTIVE_MASTERNODE_INITIAL) {} @@ -77,6 +117,8 @@ class CActiveMasternode bool UpdateSentinelPing(int version); + void DoMaintenance(CConnman &connman) { ManageState(connman); } + private: void ManageStateInitial(CConnman& connman); void ManageStateRemote(); diff --git a/src/bench/bench_absolute.cpp b/src/bench/bench_absolute.cpp index bd768180c6d8..a1d208502470 100644 --- a/src/bench/bench_absolute.cpp +++ b/src/bench/bench_absolute.cpp @@ -8,14 +8,26 @@ #include "validation.h" #include "util.h" +#include "bls/bls.h" + +void CleanupBLSTests(); +void CleanupBLSDkgTests(); + int main(int argc, char** argv) { ECC_Start(); + ECCVerifyHandle verifyHandle; + + BLSInit(); SetupEnvironment(); fPrintToDebugLog = false; // don't want to write to debug.log file benchmark::BenchRunner::RunAll(); + // need to be called before global destructors kick in (PoolAllocator is needed due to many BLSSecretKeys) + CleanupBLSDkgTests(); + CleanupBLSTests(); + ECC_Stop(); } diff --git a/src/bench/bls.cpp b/src/bench/bls.cpp new file mode 100644 index 000000000000..8127c2935a08 --- /dev/null +++ b/src/bench/bls.cpp @@ -0,0 +1,357 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bench.h" +#include "random.h" +#include "bls/bls_worker.h" +#include "utiltime.h" + +#include + +CBLSWorker blsWorker; + +void CleanupBLSTests() +{ + blsWorker.Stop(); +} + +static void BuildTestVectors(size_t count, size_t invalidCount, + BLSPublicKeyVector& pubKeys, BLSSecretKeyVector& secKeys, BLSSignatureVector& sigs, + std::vector& msgHashes, + std::vector& invalid) +{ + secKeys.resize(count); + pubKeys.resize(count); + sigs.resize(count); + msgHashes.resize(count); + + invalid.resize(count); + for (size_t i = 0; i < invalidCount; i++) { + invalid[i] = true; + } + std::random_shuffle(invalid.begin(), invalid.end()); + + for (size_t i = 0; i < count; i++) { + secKeys[i].MakeNewKey(); + pubKeys[i] = secKeys[i].GetPublicKey(); + msgHashes[i] = GetRandHash(); + sigs[i] = secKeys[i].Sign(msgHashes[i]); + + if (invalid[i]) { + CBLSSecretKey s; + s.MakeNewKey(); + sigs[i] = s.Sign(msgHashes[i]); + } + } +} + +static void BLSPubKeyAggregate_Normal(benchmark::State& state) +{ + CBLSSecretKey secKey1, secKey2; + secKey1.MakeNewKey(); + secKey2.MakeNewKey(); + CBLSPublicKey pubKey1 = secKey1.GetPublicKey(); + CBLSPublicKey pubKey2 = secKey2.GetPublicKey(); + + // Benchmark. + while (state.KeepRunning()) { + CBLSPublicKey k(pubKey1); + k.AggregateInsecure(pubKey2); + } +} + +static void BLSSecKeyAggregate_Normal(benchmark::State& state) +{ + CBLSSecretKey secKey1, secKey2; + secKey1.MakeNewKey(); + secKey2.MakeNewKey(); + CBLSPublicKey pubKey1 = secKey1.GetPublicKey(); + CBLSPublicKey pubKey2 = secKey2.GetPublicKey(); + + // Benchmark. + while (state.KeepRunning()) { + CBLSSecretKey k(secKey1); + k.AggregateInsecure(secKey2); + } +} + +static void BLSSign_Normal(benchmark::State& state) +{ + CBLSSecretKey secKey; + secKey.MakeNewKey(); + CBLSPublicKey pubKey = secKey.GetPublicKey(); + + // Benchmark. + while (state.KeepRunning()) { + uint256 hash = GetRandHash(); + secKey.Sign(hash); + } +} + +static void BLSVerify_Normal(benchmark::State& state) +{ + BLSPublicKeyVector pubKeys; + BLSSecretKeyVector secKeys; + BLSSignatureVector sigs; + std::vector msgHashes; + std::vector invalid; + BuildTestVectors(1000, 10, pubKeys, secKeys, sigs, msgHashes, invalid); + + // Benchmark. + size_t i = 0; + while (state.KeepRunning()) { + bool valid = sigs[i].VerifyInsecure(pubKeys[i], msgHashes[i]); + if (valid && invalid[i]) { + std::cout << "expected invalid but it is valid" << std::endl; + assert(false); + } else if (!valid && !invalid[i]) { + std::cout << "expected valid but it is invalid" << std::endl; + assert(false); + } + i = (i + 1) % pubKeys.size(); + } +} + + +static void BLSVerify_LargeBlock(size_t txCount, benchmark::State& state) +{ + BLSPublicKeyVector pubKeys; + BLSSecretKeyVector secKeys; + BLSSignatureVector sigs; + std::vector msgHashes; + std::vector invalid; + BuildTestVectors(txCount, 0, pubKeys, secKeys, sigs, msgHashes, invalid); + + // Benchmark. + while (state.KeepRunning()) { + for (size_t i = 0; i < pubKeys.size(); i++) { + sigs[i].VerifyInsecure(pubKeys[i], msgHashes[i]); + } + } +} + +static void BLSVerify_LargeBlock1000(benchmark::State& state) +{ + BLSVerify_LargeBlock(1000, state); +} + +static void BLSVerify_LargeBlock10000(benchmark::State& state) +{ + BLSVerify_LargeBlock(10000, state); +} + +static void BLSVerify_LargeBlockSelfAggregated(size_t txCount, benchmark::State& state) +{ + BLSPublicKeyVector pubKeys; + BLSSecretKeyVector secKeys; + BLSSignatureVector sigs; + std::vector msgHashes; + std::vector invalid; + BuildTestVectors(txCount, 0, pubKeys, secKeys, sigs, msgHashes, invalid); + + // Benchmark. + while (state.KeepRunning()) { + CBLSSignature aggSig = CBLSSignature::AggregateInsecure(sigs); + aggSig.VerifyInsecureAggregated(pubKeys, msgHashes); + } +} + +static void BLSVerify_LargeBlockSelfAggregated1000(benchmark::State& state) +{ + BLSVerify_LargeBlockSelfAggregated(1000, state); +} + +static void BLSVerify_LargeBlockSelfAggregated10000(benchmark::State& state) +{ + BLSVerify_LargeBlockSelfAggregated(10000, state); +} + +static void BLSVerify_LargeAggregatedBlock(size_t txCount, benchmark::State& state) +{ + BLSPublicKeyVector pubKeys; + BLSSecretKeyVector secKeys; + BLSSignatureVector sigs; + std::vector msgHashes; + std::vector invalid; + BuildTestVectors(txCount, 0, pubKeys, secKeys, sigs, msgHashes, invalid); + + CBLSSignature aggSig = CBLSSignature::AggregateInsecure(sigs); + + // Benchmark. + while (state.KeepRunning()) { + aggSig.VerifyInsecureAggregated(pubKeys, msgHashes); + } +} + +static void BLSVerify_LargeAggregatedBlock1000(benchmark::State& state) +{ + BLSVerify_LargeAggregatedBlock(1000, state); +} + +static void BLSVerify_LargeAggregatedBlock10000(benchmark::State& state) +{ + BLSVerify_LargeAggregatedBlock(10000, state); +} + +static void BLSVerify_LargeAggregatedBlock1000PreVerified(benchmark::State& state) +{ + BLSPublicKeyVector pubKeys; + BLSSecretKeyVector secKeys; + BLSSignatureVector sigs; + std::vector msgHashes; + std::vector invalid; + BuildTestVectors(1000, 0, pubKeys, secKeys, sigs, msgHashes, invalid); + + CBLSSignature aggSig = CBLSSignature::AggregateInsecure(sigs); + + std::set prevalidated; + + while (prevalidated.size() < 900) { + int idx = GetRandInt((int)pubKeys.size()); + if (prevalidated.count((size_t)idx)) { + continue; + } + prevalidated.emplace((size_t)idx); + } + + // Benchmark. + while (state.KeepRunning()) { + BLSPublicKeyVector nonvalidatedPubKeys; + std::vector nonvalidatedHashes; + nonvalidatedPubKeys.reserve(pubKeys.size()); + nonvalidatedHashes.reserve(msgHashes.size()); + + for (size_t i = 0; i < sigs.size(); i++) { + if (prevalidated.count(i)) { + continue; + } + nonvalidatedPubKeys.emplace_back(pubKeys[i]); + nonvalidatedHashes.emplace_back(msgHashes[i]); + } + + CBLSSignature aggSigCopy = aggSig; + for (auto idx : prevalidated) { + aggSigCopy.SubInsecure(sigs[idx]); + } + + bool valid = aggSigCopy.VerifyInsecureAggregated(nonvalidatedPubKeys, nonvalidatedHashes); + assert(valid); + } +} + +static void BLSVerify_Batched(benchmark::State& state) +{ + BLSPublicKeyVector pubKeys; + BLSSecretKeyVector secKeys; + BLSSignatureVector sigs; + std::vector msgHashes; + std::vector invalid; + BuildTestVectors(1000, 10, pubKeys, secKeys, sigs, msgHashes, invalid); + + // Benchmark. + size_t i = 0; + size_t j = 0; + size_t batchSize = 16; + while (state.KeepRunning()) { + j++; + if ((j % batchSize) != 0) { + continue; + } + + BLSPublicKeyVector testPubKeys; + BLSSignatureVector testSigs; + std::vector testMsgHashes; + testPubKeys.reserve(batchSize); + testSigs.reserve(batchSize); + testMsgHashes.reserve(batchSize); + size_t startI = i; + for (size_t k = 0; k < batchSize; k++) { + testPubKeys.emplace_back(pubKeys[i]); + testSigs.emplace_back(sigs[i]); + testMsgHashes.emplace_back(msgHashes[i]); + i = (i + 1) % pubKeys.size(); + } + + CBLSSignature batchSig = CBLSSignature::AggregateInsecure(testSigs); + bool batchValid = batchSig.VerifyInsecureAggregated(testPubKeys, testMsgHashes); + std::vector valid; + if (batchValid) { + valid.assign(batchSize, true); + } else { + for (size_t k = 0; k < batchSize; k++) { + bool valid1 = testSigs[k].VerifyInsecure(testPubKeys[k], testMsgHashes[k]); + valid.emplace_back(valid1); + } + } + for (size_t k = 0; k < batchSize; k++) { + if (valid[k] && invalid[(startI + k) % pubKeys.size()]) { + std::cout << "expected invalid but it is valid" << std::endl; + assert(false); + } else if (!valid[k] && !invalid[(startI + k) % pubKeys.size()]) { + std::cout << "expected valid but it is invalid" << std::endl; + assert(false); + } + } + } +} + +static void BLSVerify_BatchedParallel(benchmark::State& state) +{ + BLSPublicKeyVector pubKeys; + BLSSecretKeyVector secKeys; + BLSSignatureVector sigs; + std::vector msgHashes; + std::vector invalid; + BuildTestVectors(1000, 10, pubKeys, secKeys, sigs, msgHashes, invalid); + + std::list>> futures; + + volatile bool cancel = false; + auto cancelCond = [&]() { + return cancel; + }; + + // Benchmark. + size_t i = 0; + while (state.KeepRunning()) { + if (futures.size() < 100) { + while (futures.size() < 10000) { + auto f = blsWorker.AsyncVerifySig(sigs[i], pubKeys[i], msgHashes[i], cancelCond); + futures.emplace_back(std::make_pair(i, std::move(f))); + i = (i + 1) % pubKeys.size(); + } + } + + auto fp = std::move(futures.front()); + futures.pop_front(); + + size_t j = fp.first; + bool valid = fp.second.get(); + + if (valid && invalid[j]) { + std::cout << "expected invalid but it is valid" << std::endl; + assert(false); + } else if (!valid && !invalid[j]) { + std::cout << "expected valid but it is invalid" << std::endl; + assert(false); + } + } + cancel = true; + while (blsWorker.IsAsyncVerifyInProgress()) { + MilliSleep(100); + } +} + +BENCHMARK(BLSPubKeyAggregate_Normal) +BENCHMARK(BLSSecKeyAggregate_Normal) +BENCHMARK(BLSSign_Normal) +BENCHMARK(BLSVerify_Normal) +BENCHMARK(BLSVerify_LargeBlock1000) +BENCHMARK(BLSVerify_LargeBlockSelfAggregated1000) +BENCHMARK(BLSVerify_LargeBlockSelfAggregated10000) +BENCHMARK(BLSVerify_LargeAggregatedBlock1000) +BENCHMARK(BLSVerify_LargeAggregatedBlock10000) +BENCHMARK(BLSVerify_LargeAggregatedBlock1000PreVerified) +BENCHMARK(BLSVerify_Batched) +BENCHMARK(BLSVerify_BatchedParallel) diff --git a/src/bench/bls_dkg.cpp b/src/bench/bls_dkg.cpp new file mode 100644 index 000000000000..e6e473e2c7e5 --- /dev/null +++ b/src/bench/bls_dkg.cpp @@ -0,0 +1,181 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bench.h" +#include "random.h" +#include "bls/bls_worker.h" + +extern CBLSWorker blsWorker; + +struct Member { + CBLSId id; + + BLSVerificationVectorPtr vvec; + BLSSecretKeyVector skShares; +}; + +struct DKG +{ + std::vector members; + std::vector ids; + + std::vector receivedVvecs; + BLSSecretKeyVector receivedSkShares; + + BLSVerificationVectorPtr quorumVvec; + + DKG(int quorumSize) + { + members.resize(quorumSize); + ids.resize(quorumSize); + + for (int i = 0; i < quorumSize; i++) { + members[i].id.SetInt(i + 1); + ids[i] = members[i].id; + } + + for (int i = 0; i < quorumSize; i++) { + blsWorker.GenerateContributions(quorumSize / 2 + 1, ids, members[i].vvec, members[i].skShares); + } + + //printf("initialized quorum %d\n", quorumSize); + } + + void ReceiveVvecs() + { + receivedVvecs.clear(); + for (size_t i = 0; i < members.size(); i++) { + receivedVvecs.emplace_back(members[i].vvec); + } + quorumVvec = blsWorker.BuildQuorumVerificationVector(receivedVvecs); + } + + void ReceiveShares(size_t whoAmI) + { + receivedSkShares.clear(); + for (size_t i = 0; i < members.size(); i++) { + receivedSkShares.emplace_back(members[i].skShares[whoAmI]); + } + } + + void BuildQuorumVerificationVector(bool parallel) + { + quorumVvec = blsWorker.BuildQuorumVerificationVector(receivedVvecs, 0, 0, parallel); + //assert(worker.VerifyVerificationVector(*members[memberIdx].quorumVvec)); + } + + void Bench_BuildQuorumVerificationVectors(benchmark::State& state, bool parallel) + { + ReceiveVvecs(); + + while (state.KeepRunning()) { + BuildQuorumVerificationVector(parallel); + } + } + + void VerifyContributionShares(size_t whoAmI, const std::set& invalidIndexes, bool parallel, bool aggregated) + { + auto result = blsWorker.VerifyContributionShares(members[whoAmI].id, receivedVvecs, receivedSkShares, parallel, aggregated); + for (size_t i = 0; i < receivedVvecs.size(); i++) { + if (invalidIndexes.count(i)) { + assert(!result[i]); + } else { + assert(result[i]); + } + } + } + + void Bench_VerifyContributionShares(benchmark::State& state, int invalidCount, bool parallel, bool aggregated) + { + ReceiveVvecs(); + + // Benchmark. + size_t memberIdx = 0; + while (state.KeepRunning()) { + auto& m = members[memberIdx]; + + ReceiveShares(memberIdx); + + std::set invalidIndexes; + for (int i = 0; i < invalidCount; i++) { + int shareIdx = GetRandInt(receivedSkShares.size()); + receivedSkShares[shareIdx].MakeNewKey(); + invalidIndexes.emplace(shareIdx); + } + + VerifyContributionShares(memberIdx, invalidIndexes, parallel, aggregated); + + memberIdx = (memberIdx + 1) % members.size(); + } + } +}; + +std::shared_ptr dkg10; +std::shared_ptr dkg100; +std::shared_ptr dkg400; + +void InitIfNeeded() +{ + if (dkg10 == nullptr) { + dkg10 = std::make_shared(10); + } + if (dkg100 == nullptr) { + dkg100 = std::make_shared(100); + } + if (dkg400 == nullptr) { + dkg400 = std::make_shared(400); + } +} + +void CleanupBLSDkgTests() +{ + dkg10.reset(); + dkg100.reset(); + dkg400.reset(); +} + + + +#define BENCH_BuildQuorumVerificationVectors(name, quorumSize, parallel) \ + static void BLSDKG_BuildQuorumVerificationVectors_##name##_##quorumSize(benchmark::State& state) \ + { \ + InitIfNeeded(); \ + dkg##quorumSize->Bench_BuildQuorumVerificationVectors(state, parallel); \ + } \ + BENCHMARK(BLSDKG_BuildQuorumVerificationVectors_##name##_##quorumSize) + +BENCH_BuildQuorumVerificationVectors(simple, 10, false) +BENCH_BuildQuorumVerificationVectors(simple, 100, false) +BENCH_BuildQuorumVerificationVectors(simple, 400, false) +BENCH_BuildQuorumVerificationVectors(parallel, 10, true) +BENCH_BuildQuorumVerificationVectors(parallel, 100, true) +BENCH_BuildQuorumVerificationVectors(parallel, 400, true) + +/////////////////////////////// + + + +#define BENCH_VerifyContributionShares(name, quorumSize, invalidCount, parallel, aggregated) \ + static void BLSDKG_VerifyContributionShares_##name##_##quorumSize(benchmark::State& state) \ + { \ + InitIfNeeded(); \ + dkg##quorumSize->Bench_VerifyContributionShares(state, invalidCount, parallel, aggregated); \ + } \ + BENCHMARK(BLSDKG_VerifyContributionShares_##name##_##quorumSize) + +BENCH_VerifyContributionShares(simple, 10, 5, false, false) +BENCH_VerifyContributionShares(simple, 100, 5, false, false) +BENCH_VerifyContributionShares(simple, 400, 5, false, false) + +BENCH_VerifyContributionShares(aggregated, 10, 5, false, true) +BENCH_VerifyContributionShares(aggregated, 100, 5, false, true) +BENCH_VerifyContributionShares(aggregated, 400, 5, false, true) + +BENCH_VerifyContributionShares(parallel, 10, 5, true, false) +BENCH_VerifyContributionShares(parallel, 100, 5, true, false) +BENCH_VerifyContributionShares(parallel, 400, 5, true, false) + +BENCH_VerifyContributionShares(parallel_aggregated, 10, 5, true, true) +BENCH_VerifyContributionShares(parallel_aggregated, 100, 5, true, true) +BENCH_VerifyContributionShares(parallel_aggregated, 400, 5, true, true) diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 7f9b47143bfe..ac97e58ae4a4 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -131,68 +131,13 @@ static void HASH_DSHA256_2048b_single(benchmark::State& state) CHash256().Write(in.data(), in.size()).Finalize(&in[0]); } -static void HASH_X11(benchmark::State& state) -{ - uint256 hash; - std::vector in(BUFFER_SIZE,0); - while (state.KeepRunning()) - hash = HashX11(in.begin(), in.end()); -} - -static void HASH_X11_0032b_single(benchmark::State& state) -{ - uint256 hash; - std::vector in(32,0); - while (state.KeepRunning()) - hash = HashX11(in.begin(), in.end()); -} - -static void HASH_X11_0080b_single(benchmark::State& state) -{ - uint256 hash; - std::vector in(80,0); - while (state.KeepRunning()) - hash = HashX11(in.begin(), in.end()); -} - -static void HASH_X11_0128b_single(benchmark::State& state) -{ - uint256 hash; - std::vector in(128,0); - while (state.KeepRunning()) - hash = HashX11(in.begin(), in.end()); -} - -static void HASH_X11_0512b_single(benchmark::State& state) -{ - uint256 hash; - std::vector in(512,0); - while (state.KeepRunning()) - hash = HashX11(in.begin(), in.end()); -} - -static void HASH_X11_1024b_single(benchmark::State& state) -{ - uint256 hash; - std::vector in(1024,0); - while (state.KeepRunning()) - hash = HashX11(in.begin(), in.end()); -} - -static void HASH_X11_2048b_single(benchmark::State& state) -{ - uint256 hash; - std::vector in(2048,0); - while (state.KeepRunning()) - hash = HashX11(in.begin(), in.end()); -} BENCHMARK(HASH_RIPEMD160); BENCHMARK(HASH_SHA1); BENCHMARK(HASH_SHA256); BENCHMARK(HASH_DSHA256); BENCHMARK(HASH_SHA512); -BENCHMARK(HASH_X11); + BENCHMARK(HASH_SHA256_0032b); BENCHMARK(HASH_DSHA256_0032b); @@ -204,9 +149,4 @@ BENCHMARK(HASH_DSHA256_0128b_single); BENCHMARK(HASH_DSHA256_0512b_single); BENCHMARK(HASH_DSHA256_1024b_single); BENCHMARK(HASH_DSHA256_2048b_single); -BENCHMARK(HASH_X11_0032b_single); -BENCHMARK(HASH_X11_0080b_single); -BENCHMARK(HASH_X11_0128b_single); -BENCHMARK(HASH_X11_0512b_single); -BENCHMARK(HASH_X11_1024b_single); -BENCHMARK(HASH_X11_2048b_single); + diff --git a/src/bench/ecdsa.cpp b/src/bench/ecdsa.cpp new file mode 100644 index 000000000000..706608eac208 --- /dev/null +++ b/src/bench/ecdsa.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bench.h" + +#include "key.h" + +static void ECDSASign(benchmark::State& state) +{ + std::vector keys; + std::vector hashes; + for (size_t i = 0; i < 100; i++) { + CKey k; + k.MakeNewKey(false); + keys.emplace_back(k); + hashes.emplace_back(::SerializeHash((int)i)); + } + + // Benchmark. + size_t i = 0; + while (state.KeepRunning()) { + std::vector sig; + keys[i].Sign(hashes[i], sig); + i = (i + 1) % keys.size(); + } +} + +static void ECDSAVerify(benchmark::State& state) +{ + std::vector keys; + std::vector hashes; + std::vector> sigs; + for (size_t i = 0; i < 100; i++) { + CKey k; + k.MakeNewKey(false); + keys.emplace_back(k.GetPubKey()); + hashes.emplace_back(::SerializeHash((int)i)); + std::vector sig; + k.Sign(hashes[i], sig); + sigs.emplace_back(sig); + } + + // Benchmark. + size_t i = 0; + while (state.KeepRunning()) { + keys[i].Verify(hashes[i], sigs[i]); + i = (i + 1) % keys.size(); + } +} + +static void ECDSAVerify_LargeBlock(benchmark::State& state) +{ + std::vector keys; + std::vector hashes; + std::vector> sigs; + for (size_t i = 0; i < 1000; i++) { + CKey k; + k.MakeNewKey(false); + keys.emplace_back(k.GetPubKey()); + hashes.emplace_back(::SerializeHash((int)i)); + std::vector sig; + k.Sign(hashes[i], sig); + sigs.emplace_back(sig); + } + + // Benchmark. + while (state.KeepRunning()) { + for (size_t i = 0; i < keys.size(); i++) { + keys[i].Verify(hashes[i], sigs[i]); + } + } +} + +BENCHMARK(ECDSASign) +BENCHMARK(ECDSAVerify) +BENCHMARK(ECDSAVerify_LargeBlock) diff --git a/src/bls/bls.cpp b/src/bls/bls.cpp new file mode 100644 index 000000000000..83cac42f7711 --- /dev/null +++ b/src/bls/bls.cpp @@ -0,0 +1,489 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bls.h" + +#include "hash.h" +#include "random.h" +#include "tinyformat.h" + +#ifndef BUILD_BITCOIN_INTERNAL +#include "support/allocators/secure.h" +#include +#endif + +#include +#include + +bool CBLSId::InternalSetBuf(const void* buf) +{ + memcpy(impl.begin(), buf, sizeof(uint256)); + return true; +} + +bool CBLSId::InternalGetBuf(void* buf) const +{ + memcpy(buf, impl.begin(), sizeof(uint256)); + return true; +} + +void CBLSId::SetInt(int x) +{ + impl.SetHex(strprintf("%x", x)); + fValid = true; + UpdateHash(); +} + +void CBLSId::SetHash(const uint256& hash) +{ + impl = hash; + fValid = true; + UpdateHash(); +} + +CBLSId CBLSId::FromInt(int64_t i) +{ + CBLSId id; + id.SetInt(i); + return id; +} + +CBLSId CBLSId::FromHash(const uint256& hash) +{ + CBLSId id; + id.SetHash(hash); + return id; +} + +bool CBLSSecretKey::InternalSetBuf(const void* buf) +{ + try { + impl = bls::PrivateKey::FromBytes((const uint8_t*)buf); + return true; + } catch (...) { + return false; + } +} + +bool CBLSSecretKey::InternalGetBuf(void* buf) const +{ + impl.Serialize((uint8_t*)buf); + return true; +} + +void CBLSSecretKey::AggregateInsecure(const CBLSSecretKey& o) +{ + assert(IsValid() && o.IsValid()); + impl = bls::PrivateKey::AggregateInsecure({impl, o.impl}); + UpdateHash(); +} + +CBLSSecretKey CBLSSecretKey::AggregateInsecure(const std::vector& sks) +{ + if (sks.empty()) { + return CBLSSecretKey(); + } + + std::vector v; + v.reserve(sks.size()); + for (auto& sk : sks) { + v.emplace_back(sk.impl); + } + + auto agg = bls::PrivateKey::AggregateInsecure(v); + CBLSSecretKey ret; + ret.impl = agg; + ret.fValid = true; + ret.UpdateHash(); + return ret; +} + +#ifndef BUILD_BITCOIN_INTERNAL +void CBLSSecretKey::MakeNewKey() +{ + unsigned char buf[32]; + while (true) { + GetStrongRandBytes(buf, sizeof(buf)); + try { + impl = bls::PrivateKey::FromBytes((const uint8_t*)buf); + break; + } catch (...) { + } + } + fValid = true; + UpdateHash(); +} +#endif + +bool CBLSSecretKey::SecretKeyShare(const std::vector& msk, const CBLSId& _id) +{ + fValid = false; + UpdateHash(); + + if (!_id.IsValid()) { + return false; + } + + std::vector mskVec; + mskVec.reserve(msk.size()); + for (const CBLSSecretKey& sk : msk) { + if (!sk.IsValid()) { + return false; + } + mskVec.emplace_back(sk.impl); + } + + try { + impl = bls::BLS::PrivateKeyShare(mskVec, (const uint8_t*)_id.impl.begin()); + } catch (...) { + return false; + } + + fValid = true; + UpdateHash(); + return true; +} + +CBLSPublicKey CBLSSecretKey::GetPublicKey() const +{ + if (!IsValid()) { + return CBLSPublicKey(); + } + + CBLSPublicKey pubKey; + pubKey.impl = impl.GetPublicKey(); + pubKey.fValid = true; + pubKey.UpdateHash(); + return pubKey; +} + +CBLSSignature CBLSSecretKey::Sign(const uint256& hash) const +{ + if (!IsValid()) { + return CBLSSignature(); + } + + CBLSSignature sigRet; + sigRet.impl = impl.SignInsecurePrehashed((const uint8_t*)hash.begin()); + + sigRet.fValid = true; + sigRet.UpdateHash(); + + return sigRet; +} + +bool CBLSPublicKey::InternalSetBuf(const void* buf) +{ + try { + impl = bls::PublicKey::FromBytes((const uint8_t*)buf); + return true; + } catch (...) { + return false; + } +} + +bool CBLSPublicKey::InternalGetBuf(void* buf) const +{ + impl.Serialize((uint8_t*)buf); + return true; +} + +void CBLSPublicKey::AggregateInsecure(const CBLSPublicKey& o) +{ + assert(IsValid() && o.IsValid()); + impl = bls::PublicKey::AggregateInsecure({impl, o.impl}); + UpdateHash(); +} + +CBLSPublicKey CBLSPublicKey::AggregateInsecure(const std::vector& pks) +{ + if (pks.empty()) { + return CBLSPublicKey(); + } + + std::vector v; + v.reserve(pks.size()); + for (auto& pk : pks) { + v.emplace_back(pk.impl); + } + + auto agg = bls::PublicKey::AggregateInsecure(v); + CBLSPublicKey ret; + ret.impl = agg; + ret.fValid = true; + ret.UpdateHash(); + return ret; +} + +bool CBLSPublicKey::PublicKeyShare(const std::vector& mpk, const CBLSId& _id) +{ + fValid = false; + UpdateHash(); + + if (!_id.IsValid()) { + return false; + } + + std::vector mpkVec; + mpkVec.reserve(mpk.size()); + for (const CBLSPublicKey& pk : mpk) { + if (!pk.IsValid()) { + return false; + } + mpkVec.emplace_back(pk.impl); + } + + try { + impl = bls::BLS::PublicKeyShare(mpkVec, (const uint8_t*)_id.impl.begin()); + } catch (...) { + return false; + } + + fValid = true; + UpdateHash(); + return true; +} + +bool CBLSPublicKey::DHKeyExchange(const CBLSSecretKey& sk, const CBLSPublicKey& pk) +{ + fValid = false; + UpdateHash(); + + if (!sk.IsValid() || !pk.IsValid()) { + return false; + } + impl = bls::BLS::DHKeyExchange(sk.impl, pk.impl); + fValid = true; + UpdateHash(); + return true; +} + +bool CBLSSignature::InternalSetBuf(const void* buf) +{ + try { + impl = bls::InsecureSignature::FromBytes((const uint8_t*)buf); + return true; + } catch (...) { + return false; + } +} + +bool CBLSSignature::InternalGetBuf(void* buf) const +{ + impl.Serialize((uint8_t*)buf); + return true; +} + +void CBLSSignature::AggregateInsecure(const CBLSSignature& o) +{ + assert(IsValid() && o.IsValid()); + impl = bls::InsecureSignature::Aggregate({impl, o.impl}); + UpdateHash(); +} + +CBLSSignature CBLSSignature::AggregateInsecure(const std::vector& sigs) +{ + if (sigs.empty()) { + return CBLSSignature(); + } + + std::vector v; + v.reserve(sigs.size()); + for (auto& pk : sigs) { + v.emplace_back(pk.impl); + } + + auto agg = bls::InsecureSignature::Aggregate(v); + CBLSSignature ret; + ret.impl = agg; + ret.fValid = true; + ret.UpdateHash(); + return ret; +} + +CBLSSignature CBLSSignature::AggregateSecure(const std::vector& sigs, + const std::vector& pks, + const uint256& hash) +{ + if (sigs.size() != pks.size() || sigs.empty()) { + return CBLSSignature(); + } + + std::vector v; + v.reserve(sigs.size()); + + for (size_t i = 0; i < sigs.size(); i++) { + bls::AggregationInfo aggInfo = bls::AggregationInfo::FromMsgHash(pks[i].impl, hash.begin()); + v.emplace_back(bls::Signature::FromInsecureSig(sigs[i].impl, aggInfo)); + } + + auto aggSig = bls::Signature::AggregateSigs(v); + CBLSSignature ret; + ret.impl = aggSig.GetInsecureSig(); + ret.fValid = true; + ret.UpdateHash(); + return ret; +} + +void CBLSSignature::SubInsecure(const CBLSSignature& o) +{ + assert(IsValid() && o.IsValid()); + impl = impl.DivideBy({o.impl}); + UpdateHash(); +} + +bool CBLSSignature::VerifyInsecure(const CBLSPublicKey& pubKey, const uint256& hash) const +{ + if (!IsValid() || !pubKey.IsValid()) { + return false; + } + + try { + return impl.Verify({(const uint8_t*)hash.begin()}, {pubKey.impl}); + } catch (...) { + return false; + } +} + +bool CBLSSignature::VerifyInsecureAggregated(const std::vector& pubKeys, const std::vector& hashes) const +{ + if (!IsValid()) { + return false; + } + assert(!pubKeys.empty() && !hashes.empty() && pubKeys.size() == hashes.size()); + + std::vector pubKeyVec; + std::vector hashes2; + hashes2.reserve(hashes.size()); + pubKeyVec.reserve(pubKeys.size()); + for (size_t i = 0; i < pubKeys.size(); i++) { + auto& p = pubKeys[i]; + if (!p.IsValid()) { + return false; + } + pubKeyVec.push_back(p.impl); + hashes2.push_back((uint8_t*)hashes[i].begin()); + } + + try { + return impl.Verify(hashes2, pubKeyVec); + } catch (...) { + return false; + } +} + +bool CBLSSignature::VerifySecureAggregated(const std::vector& pks, const uint256& hash) const +{ + if (pks.empty()) { + return false; + } + + std::vector v; + v.reserve(pks.size()); + for (auto& pk : pks) { + auto aggInfo = bls::AggregationInfo::FromMsgHash(pk.impl, hash.begin()); + v.emplace_back(aggInfo); + } + + bls::AggregationInfo aggInfo = bls::AggregationInfo::MergeInfos(v); + bls::Signature aggSig = bls::Signature::FromInsecureSig(impl, aggInfo); + return aggSig.Verify(); +} + +bool CBLSSignature::Recover(const std::vector& sigs, const std::vector& ids) +{ + fValid = false; + UpdateHash(); + + if (sigs.empty() || ids.empty() || sigs.size() != ids.size()) { + return false; + } + + std::vector sigsVec; + std::vector idsVec; + sigsVec.reserve(sigs.size()); + idsVec.reserve(sigs.size()); + + for (size_t i = 0; i < sigs.size(); i++) { + if (!sigs[i].IsValid() || !ids[i].IsValid()) { + return false; + } + sigsVec.emplace_back(sigs[i].impl); + idsVec.emplace_back(ids[i].impl.begin()); + } + + try { + impl = bls::BLS::RecoverSig(sigsVec, idsVec); + } catch (...) { + return false; + } + + fValid = true; + UpdateHash(); + return true; +} + +#ifndef BUILD_BITCOIN_INTERNAL +struct secure_user_allocator { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + + static char* malloc(const size_type bytes) + { + return static_cast(LockedPoolManager::Instance().alloc(bytes)); + } + + static void free(char* const block) + { + LockedPoolManager::Instance().free(block); + } +}; + +// every thread has it's own pool allocator for secure data to speed things up +// otherwise locking of mutexes slows down the system at places were you'd never expect it +// downside is that we must make sure that all threads have destroyed their copy of the pool before the global +// LockedPool is destroyed. This means that all worker threads must finish before static destruction begins +// we use sizeof(bn_t) as the pool request size as this is what Chia's BLS library will request in most cases +// In case something larger is requested, we directly call into LockedPool and accept the slowness +thread_local static boost::pool securePool(sizeof(bn_t) + sizeof(size_t)); + +static void* secure_allocate(size_t n) +{ + void* p; + if (n <= securePool.get_requested_size() - sizeof(size_t)) { + p = securePool.ordered_malloc(); + } else { + p = secure_user_allocator::malloc(n + sizeof(size_t)); + } + *(size_t*)p = n; + p = (uint8_t*)p + sizeof(size_t); + return p; +} + +static void secure_free(void* p) +{ + if (!p) { + return; + } + p = (uint8_t*)p - sizeof(size_t); + size_t n = *(size_t*)p; + memory_cleanse(p, n + sizeof(size_t)); + if (n <= securePool.get_requested_size() - sizeof(size_t)) { + securePool.ordered_free(p); + } else { + secure_user_allocator::free((char*)p); + } +} +#endif + +bool BLSInit() +{ + if (!bls::BLS::Init()) { + return false; + } +#ifndef BUILD_BITCOIN_INTERNAL + bls::BLS::SetSecureAllocator(secure_allocate, secure_free); +#endif + return true; +} diff --git a/src/bls/bls.h b/src/bls/bls.h new file mode 100644 index 000000000000..4e0e40d06f79 --- /dev/null +++ b/src/bls/bls.h @@ -0,0 +1,305 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ABSOLUTE_CRYPTO_BLS_H +#define ABSOLUTE_CRYPTO_BLS_H + +#include "hash.h" +#include "serialize.h" +#include "uint256.h" +#include "utilstrencodings.h" + +#undef ERROR // chia BLS uses relic, which defines ERROR, which in turn causes win32/win64 builds to print many warnings +#include +#include +#include +#include +#undef DOUBLE + +#include +#include + +// reversed BLS12-381 +#define BLS_CURVE_ID_SIZE 32 +#define BLS_CURVE_SECKEY_SIZE 32 +#define BLS_CURVE_PUBKEY_SIZE 48 +#define BLS_CURVE_SIG_SIZE 96 + +class CBLSSignature; +class CBLSPublicKey; + +template +class CBLSWrapper +{ + friend class CBLSSecretKey; + friend class CBLSPublicKey; + friend class CBLSSignature; + +protected: + ImplType impl; + bool fValid{false}; + mutable uint256 cachedHash; + + inline constexpr size_t GetSerSize() const { return SerSize; } + + virtual bool InternalSetBuf(const void* buf) = 0; + virtual bool InternalGetBuf(void* buf) const = 0; + +public: + static const size_t SerSize = _SerSize; + + CBLSWrapper() + { + UpdateHash(); + } + + CBLSWrapper(const CBLSWrapper& ref) = default; + CBLSWrapper& operator=(const CBLSWrapper& ref) = default; + CBLSWrapper(CBLSWrapper&& ref) + { + std::swap(impl, ref.impl); + std::swap(fValid, ref.fValid); + std::swap(cachedHash, ref.cachedHash); + } + CBLSWrapper& operator=(CBLSWrapper&& ref) + { + std::swap(impl, ref.impl); + std::swap(fValid, ref.fValid); + std::swap(cachedHash, ref.cachedHash); + return *this; + } + + bool operator==(const C& r) const + { + return fValid == r.fValid && impl == r.impl; + } + bool operator!=(const C& r) const + { + return !((*this) == r); + } + + bool IsValid() const + { + return fValid; + } + + void SetBuf(const void* buf, size_t size) + { + if (size != SerSize) { + Reset(); + return; + } + + if (std::all_of((const char*)buf, (const char*)buf + SerSize, [](char c) { return c == 0; })) { + Reset(); + } else { + fValid = InternalSetBuf(buf); + if (!fValid) { + Reset(); + } + } + UpdateHash(); + } + + void Reset() + { + *((C*)this) = C(); + } + + void GetBuf(void* buf, size_t size) const + { + assert(size == SerSize); + + if (!fValid) { + memset(buf, 0, SerSize); + } else { + bool ok = InternalGetBuf(buf); + assert(ok); + } + } + + template + void SetBuf(const T& buf) + { + SetBuf(buf.data(), buf.size()); + } + + template + void GetBuf(T& buf) const + { + buf.resize(GetSerSize()); + GetBuf(buf.data(), buf.size()); + } + + const uint256& GetHash() const + { + return cachedHash; + } + + void UpdateHash() const + { + cachedHash = ::SerializeHash(*this); + } + + bool SetHexStr(const std::string& str) + { + auto b = ParseHex(str); + if (b.size() != SerSize) { + return false; + } + SetBuf(b); + return IsValid(); + } + +public: + template + inline void Serialize(Stream& s) const + { + char buf[SerSize] = {0}; + GetBuf(buf, SerSize); + s.write((const char*)buf, SerSize); + + // if (s.GetType() != SER_GETHASH) { + // CheckMalleable(buf, SerSize); + // } + } + template + inline void Unserialize(Stream& s) + { + char buf[SerSize]; + s.read((char*)buf, SerSize); + SetBuf(buf, SerSize); + + CheckMalleable(buf, SerSize); + } + + inline void CheckMalleable(void* buf, size_t size) const + { + char buf2[SerSize]; + C tmp; + tmp.SetBuf(buf, SerSize); + tmp.GetBuf(buf2, SerSize); + if (memcmp(buf, buf2, SerSize)) { + // TODO not sure if this is actually possible with the BLS libs. I'm assuming here that somewhere deep inside + // these libs masking might happen, so that 2 different binary representations could result in the same object + // representation + throw std::ios_base::failure("malleable BLS object"); + } + } + + inline std::string ToString() const + { + std::vector buf; + GetBuf(buf); + return HexStr(buf.begin(), buf.end()); + } +}; + +class CBLSId : public CBLSWrapper +{ +public: + using CBLSWrapper::operator=; + using CBLSWrapper::operator==; + using CBLSWrapper::operator!=; + + void SetInt(int x); + void SetHash(const uint256& hash); + + static CBLSId FromInt(int64_t i); + static CBLSId FromHash(const uint256& hash); + +protected: + bool InternalSetBuf(const void* buf); + bool InternalGetBuf(void* buf) const; +}; + +class CBLSSecretKey : public CBLSWrapper +{ +public: + using CBLSWrapper::operator=; + using CBLSWrapper::operator==; + using CBLSWrapper::operator!=; + + void AggregateInsecure(const CBLSSecretKey& o); + static CBLSSecretKey AggregateInsecure(const std::vector& sks); + +#ifndef BUILD_BITCOIN_INTERNAL + void MakeNewKey(); +#endif + bool SecretKeyShare(const std::vector& msk, const CBLSId& id); + + CBLSPublicKey GetPublicKey() const; + CBLSSignature Sign(const uint256& hash) const; + +protected: + bool InternalSetBuf(const void* buf); + bool InternalGetBuf(void* buf) const; +}; + +class CBLSPublicKey : public CBLSWrapper +{ + friend class CBLSSecretKey; + friend class CBLSSignature; + +public: + using CBLSWrapper::operator=; + using CBLSWrapper::operator==; + using CBLSWrapper::operator!=; + + void AggregateInsecure(const CBLSPublicKey& o); + static CBLSPublicKey AggregateInsecure(const std::vector& pks); + + bool PublicKeyShare(const std::vector& mpk, const CBLSId& id); + bool DHKeyExchange(const CBLSSecretKey& sk, const CBLSPublicKey& pk); + +protected: + bool InternalSetBuf(const void* buf); + bool InternalGetBuf(void* buf) const; +}; + +class CBLSSignature : public CBLSWrapper +{ + friend class CBLSSecretKey; + +public: + using CBLSWrapper::operator==; + using CBLSWrapper::operator!=; + + CBLSSignature() = default; + CBLSSignature(const CBLSSignature&) = default; + CBLSSignature& operator=(const CBLSSignature&) = default; + + void AggregateInsecure(const CBLSSignature& o); + static CBLSSignature AggregateInsecure(const std::vector& sigs); + static CBLSSignature AggregateSecure(const std::vector& sigs, const std::vector& pks, const uint256& hash); + + void SubInsecure(const CBLSSignature& o); + + bool VerifyInsecure(const CBLSPublicKey& pubKey, const uint256& hash) const; + bool VerifyInsecureAggregated(const std::vector& pubKeys, const std::vector& hashes) const; + + bool VerifySecureAggregated(const std::vector& pks, const uint256& hash) const; + + bool Recover(const std::vector& sigs, const std::vector& ids); + +protected: + bool InternalSetBuf(const void* buf); + bool InternalGetBuf(void* buf) const; +}; + +typedef std::vector BLSIdVector; +typedef std::vector BLSVerificationVector; +typedef std::vector BLSPublicKeyVector; +typedef std::vector BLSSecretKeyVector; +typedef std::vector BLSSignatureVector; + +typedef std::shared_ptr BLSIdVectorPtr; +typedef std::shared_ptr BLSVerificationVectorPtr; +typedef std::shared_ptr BLSPublicKeyVectorPtr; +typedef std::shared_ptr BLSSecretKeyVectorPtr; +typedef std::shared_ptr BLSSignatureVectorPtr; + +bool BLSInit(); + +#endif // ABSOLUTE_CRYPTO_BLS_H diff --git a/src/bls/bls_ies.cpp b/src/bls/bls_ies.cpp new file mode 100644 index 000000000000..2c67b9737892 --- /dev/null +++ b/src/bls/bls_ies.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bls_ies.h" + +#include "hash.h" +#include "random.h" +#include "streams.h" + +#include "crypto/aes.h" + +template +static bool EncryptBlob(const void* in, size_t inSize, Out& out, const void* symKey, const void* iv) +{ + out.resize(inSize); + + AES256CBCEncrypt enc((const unsigned char*)symKey, (const unsigned char*)iv, false); + int w = enc.Encrypt((const unsigned char*)in, (int)inSize, (unsigned char*)out.data()); + return w == (int)inSize; +} + +template +static bool DecryptBlob(const void* in, size_t inSize, Out& out, const void* symKey, const void* iv) +{ + out.resize(inSize); + + AES256CBCDecrypt enc((const unsigned char*)symKey, (const unsigned char*)iv, false); + int w = enc.Decrypt((const unsigned char*)in, (int)inSize, (unsigned char*)out.data()); + return w == (int)inSize; +} + +bool CBLSIESEncryptedBlob::Encrypt(const CBLSPublicKey& peerPubKey, const void* plainTextData, size_t dataSize) +{ + CBLSSecretKey ephemeralSecretKey; + ephemeralSecretKey.MakeNewKey(); + ephemeralPubKey = ephemeralSecretKey.GetPublicKey(); + GetStrongRandBytes(iv, sizeof(iv)); + + CBLSPublicKey pk; + if (!pk.DHKeyExchange(ephemeralSecretKey, peerPubKey)) { + return false; + } + + std::vector symKey; + pk.GetBuf(symKey); + symKey.resize(32); + + return EncryptBlob(plainTextData, dataSize, data, symKey.data(), iv); +} + +bool CBLSIESEncryptedBlob::Decrypt(const CBLSSecretKey& secretKey, CDataStream& decryptedDataRet) const +{ + CBLSPublicKey pk; + if (!pk.DHKeyExchange(secretKey, ephemeralPubKey)) { + return false; + } + + std::vector symKey; + pk.GetBuf(symKey); + symKey.resize(32); + + return DecryptBlob(data.data(), data.size(), decryptedDataRet, symKey.data(), iv); +} + + +bool CBLSIESMultiRecipientBlobs::Encrypt(const std::vector& recipients, const BlobVector& _blobs) +{ + if (recipients.size() != _blobs.size()) { + return false; + } + + InitEncrypt(_blobs.size()); + + for (size_t i = 0; i < _blobs.size(); i++) { + if (!Encrypt(i, recipients[i], _blobs[i])) { + return false; + } + } + + return true; +} + +void CBLSIESMultiRecipientBlobs::InitEncrypt(size_t count) +{ + ephemeralSecretKey.MakeNewKey(); + ephemeralPubKey = ephemeralSecretKey.GetPublicKey(); + GetStrongRandBytes(ivSeed.begin(), ivSeed.size()); + + uint256 iv = ivSeed; + ivVector.resize(count); + blobs.resize(count); + for (size_t i = 0; i < count; i++) { + ivVector[i] = iv; + iv = ::SerializeHash(iv); + } +} + +bool CBLSIESMultiRecipientBlobs::Encrypt(size_t idx, const CBLSPublicKey& recipient, const Blob& blob) +{ + assert(idx < blobs.size()); + + CBLSPublicKey pk; + if (!pk.DHKeyExchange(ephemeralSecretKey, recipient)) { + return false; + } + + std::vector symKey; + pk.GetBuf(symKey); + symKey.resize(32); + + return EncryptBlob(blob.data(), blob.size(), blobs[idx], symKey.data(), ivVector[idx].begin()); +} + +bool CBLSIESMultiRecipientBlobs::Decrypt(size_t idx, const CBLSSecretKey& sk, Blob& blobRet) const +{ + if (idx >= blobs.size()) { + return false; + } + + CBLSPublicKey pk; + if (!pk.DHKeyExchange(sk, ephemeralPubKey)) { + return false; + } + + std::vector symKey; + pk.GetBuf(symKey); + symKey.resize(32); + + uint256 iv = ivSeed; + for (size_t i = 0; i < idx; i++) { + iv = ::SerializeHash(iv); + } + + return DecryptBlob(blobs[idx].data(), blobs[idx].size(), blobRet, symKey.data(), iv.begin()); +} diff --git a/src/bls/bls_ies.h b/src/bls/bls_ies.h new file mode 100644 index 000000000000..5f28c91aa269 --- /dev/null +++ b/src/bls/bls_ies.h @@ -0,0 +1,164 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef DASH_CRYPTO_BLS_IES_H +#define DASH_CRYPTO_BLS_IES_H + +#include "bls.h" +#include "streams.h" + +class CBLSIESEncryptedBlob +{ +public: + CBLSPublicKey ephemeralPubKey; + unsigned char iv[16]; + std::vector data; + + bool valid{false}; + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + if (!ser_action.ForRead()) { + assert(valid); + } else { + valid = false; + } + READWRITE(ephemeralPubKey); + READWRITE(FLATDATA(iv)); + READWRITE(data); + if (ser_action.ForRead()) { + valid = true; + } + }; + +public: + bool Encrypt(const CBLSPublicKey& peerPubKey, const void* data, size_t dataSize); + bool Decrypt(const CBLSSecretKey& secretKey, CDataStream& decryptedDataRet) const; +}; + +template +class CBLSIESEncryptedObject : public CBLSIESEncryptedBlob +{ +public: + CBLSIESEncryptedObject() + { + } + + bool Encrypt(const CBLSPublicKey& peerPubKey, const Object& obj, int nVersion) + { + try { + CDataStream ds(SER_NETWORK, nVersion); + ds << obj; + return CBLSIESEncryptedBlob::Encrypt(peerPubKey, ds.data(), ds.size()); + } catch (std::exception&) { + return false; + } + } + + bool Decrypt(const CBLSSecretKey& secretKey, Object& objRet, int nVersion) const + { + CDataStream ds(SER_NETWORK, nVersion); + if (!CBLSIESEncryptedBlob::Decrypt(secretKey, ds)) { + return false; + } + try { + ds >> objRet; + } catch (std::exception& e) { + return false; + } + return true; + } +}; + +class CBLSIESMultiRecipientBlobs +{ +public: + typedef std::vector Blob; + typedef std::vector BlobVector; + +public: + CBLSPublicKey ephemeralPubKey; + uint256 ivSeed; + BlobVector blobs; + + // Used while encrypting. Temporary and only in-memory + CBLSSecretKey ephemeralSecretKey; + std::vector ivVector; + +public: + bool Encrypt(const std::vector& recipients, const BlobVector& _blobs); + + void InitEncrypt(size_t count); + bool Encrypt(size_t idx, const CBLSPublicKey& recipient, const Blob& blob); + bool Decrypt(size_t idx, const CBLSSecretKey& sk, Blob& blobRet) const; + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(ephemeralPubKey); + READWRITE(ivSeed); + READWRITE(blobs); + } +}; + +template +class CBLSIESMultiRecipientObjects : public CBLSIESMultiRecipientBlobs +{ +public: + typedef std::vector ObjectVector; + +public: + bool Encrypt(const std::vector& recipients, const ObjectVector& _objects, int nVersion) + { + BlobVector blobs; + blobs.resize(_objects.size()); + + try { + CDataStream ds(SER_NETWORK, nVersion); + for (size_t i = 0; i < _objects.size(); i++) { + ds.clear(); + + ds << _objects[i]; + blobs[i].assign(ds.begin(), ds.end()); + } + } catch (std::exception&) { + return false; + } + + return CBLSIESMultiRecipientBlobs::Encrypt(recipients, blobs); + } + + bool Encrypt(size_t idx, const CBLSPublicKey& recipient, const Object& obj, int nVersion) + { + CDataStream ds(SER_NETWORK, nVersion); + ds << obj; + Blob blob(ds.begin(), ds.end()); + return CBLSIESMultiRecipientBlobs::Encrypt(idx, recipient, blob); + } + + bool Decrypt(size_t idx, const CBLSSecretKey& sk, Object& objectRet, int nVersion) const + { + Blob blob; + if (!CBLSIESMultiRecipientBlobs::Decrypt(idx, sk, blob)) { + return false; + } + + try { + CDataStream ds(blob, SER_NETWORK, nVersion); + ds >> objectRet; + return true; + } catch (std::exception&) { + return false; + } + } +}; + +#endif // DASH_CRYPTO_BLS_IES_H diff --git a/src/bls/bls_worker.cpp b/src/bls/bls_worker.cpp new file mode 100644 index 000000000000..f93d8504b2f4 --- /dev/null +++ b/src/bls/bls_worker.cpp @@ -0,0 +1,957 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bls_worker.h" +#include "hash.h" +#include "serialize.h" + +#include "util.h" + +template +bool VerifyVectorHelper(const std::vector& vec, size_t start, size_t count) +{ + if (start == 0 && count == 0) { + count = vec.size(); + } + std::set set; + for (size_t i = start; i < start + count; i++) { + if (!vec[i].IsValid()) + return false; + // check duplicates + if (!set.emplace(vec[i].GetHash()).second) { + return false; + } + } + return true; +} + +// Creates a doneCallback and a future. The doneCallback simply finishes the future +template +std::pair, std::future > BuildFutureDoneCallback() +{ + auto p = std::make_shared >(); + std::function f = [p](const T& v) { + p->set_value(v); + }; + return std::make_pair(std::move(f), p->get_future()); +} +template +std::pair, std::future > BuildFutureDoneCallback2() +{ + auto p = std::make_shared >(); + std::function f = [p](T v) { + p->set_value(v); + }; + return std::make_pair(std::move(f), p->get_future()); +} + + +///// + +CBLSWorker::CBLSWorker() +{ + int workerCount = std::thread::hardware_concurrency() / 2; + workerCount = std::max(std::min(1, workerCount), 4); + workerPool.resize(workerCount); + + RenameThreadPool(workerPool, "bls-worker"); +} + +CBLSWorker::~CBLSWorker() +{ + Stop(); +} + +void CBLSWorker::Stop() +{ + workerPool.clear_queue(); + workerPool.stop(true); +} + +bool CBLSWorker::GenerateContributions(int quorumThreshold, const BLSIdVector& ids, BLSVerificationVectorPtr& vvecRet, BLSSecretKeyVector& skShares) +{ + BLSSecretKeyVectorPtr svec = std::make_shared((size_t)quorumThreshold); + vvecRet = std::make_shared((size_t)quorumThreshold); + skShares.resize(ids.size()); + + for (int i = 0; i < quorumThreshold; i++) { + (*svec)[i].MakeNewKey(); + } + std::list > futures; + size_t batchSize = 8; + + for (size_t i = 0; i < quorumThreshold; i += batchSize) { + size_t start = i; + size_t count = std::min(batchSize, quorumThreshold - start); + auto f = [&, start, count](int threadId) { + for (size_t j = start; j < start + count; j++) { + (*vvecRet)[j] = (*svec)[j].GetPublicKey(); + } + return true; + }; + futures.emplace_back(workerPool.push(f)); + } + + for (size_t i = 0; i < ids.size(); i += batchSize) { + size_t start = i; + size_t count = std::min(batchSize, ids.size() - start); + auto f = [&, start, count](int threadId) { + for (size_t j = start; j < start + count; j++) { + if (!skShares[j].SecretKeyShare(*svec, ids[j])) { + return false; + } + } + return true; + }; + futures.emplace_back(workerPool.push(f)); + } + bool success = true; + for (auto& f : futures) { + if (!f.get()) { + success = false; + } + } + return success; +} + +// aggregates a single vector of BLS objects in parallel +// the input vector is split into batches and each batch is aggregated in parallel +// when enough batches are finished to form a new batch, the new batch is queued for further parallel aggregation +// when no more batches can be created from finished batch results, the final aggregated is created and the doneCallback +// called. +// The Aggregator object needs to be created on the heap and it will delete itself after calling the doneCallback +// The input vector is not copied into the Aggregator but instead a vector of pointers to the original entries from the +// input vector is stored. This means that the input vector must stay alive for the whole lifetime of the Aggregator +template +struct Aggregator { + typedef T ElementType; + + size_t batchSize{16}; + std::shared_ptr > inputVec; + + bool parallel; + ctpl::thread_pool& workerPool; + + std::mutex m; + // items in the queue are all intermediate aggregation results of finished batches. + // The intermediate results must be deleted by us again (which we do in SyncAggregateAndPushAggQueue) + boost::lockfree::queue aggQueue; + std::atomic aggQueueSize{0}; + + // keeps track of currently queued/in-progress batches. If it reaches 0, we are done + std::atomic waitCount{0}; + + typedef std::function DoneCallback; + DoneCallback doneCallback; + + // TP can either be a pointer or a reference + template + Aggregator(const std::vector& _inputVec, + size_t start, size_t count, + bool _parallel, + ctpl::thread_pool& _workerPool, + DoneCallback _doneCallback) : + workerPool(_workerPool), + parallel(_parallel), + aggQueue(0), + doneCallback(std::move(_doneCallback)) + { + inputVec = std::make_shared >(count); + for (size_t i = 0; i < count; i++) { + (*inputVec)[i] = pointer(_inputVec[start + i]); + } + } + + const T* pointer(const T& v) { return &v; } + const T* pointer(const T* v) { return v; } + + // Starts aggregation. + // If parallel=true, then this will return fast, otherwise this will block until aggregation is done + void Start() + { + size_t batchCount = (inputVec->size() + batchSize - 1) / batchSize; + + if (!parallel) { + if (inputVec->size() == 1) { + doneCallback(*(*inputVec)[0]); + } else { + doneCallback(SyncAggregate(*inputVec, 0, inputVec->size())); + } + delete this; + return; + } + + if (batchCount == 1) { + // just a single batch of work, take a shortcut. + PushWork([this](int threadId) { + if (inputVec->size() == 1) { + doneCallback(*(*inputVec)[0]); + } else { + doneCallback(SyncAggregate(*inputVec, 0, inputVec->size())); + } + delete this; + }); + return; + } + + // increment wait counter as otherwise the first finished async aggregation might signal that we're done + IncWait(); + for (size_t i = 0; i < batchCount; i++) { + size_t start = i * batchSize; + size_t count = std::min(batchSize, inputVec->size() - start); + AsyncAggregateAndPushAggQueue(inputVec, start, count, false); + } + // this will decrement the wait counter and in most cases NOT finish, as async work is still in progress + CheckDone(); + } + + void IncWait() + { + ++waitCount; + } + + void CheckDone() + { + if (--waitCount == 0) { + Finish(); + } + } + + void Finish() + { + // All async work is done, but we might have items in the aggQueue which are the results of the async + // work. This is the case when these did not add up to a new batch. In this case, we have to aggregate + // the items into the final result + + std::vector rem(aggQueueSize); + for (size_t i = 0; i < rem.size(); i++) { + T* p = nullptr; + bool s = aggQueue.pop(p); + assert(s); + rem[i] = p; + } + + T r; + if (rem.size() == 1) { + // just one intermediate result, which is actually the final result + r = *rem[0]; + } else { + // multiple intermediate results left which did not add up to a new batch. aggregate them now + r = SyncAggregate(rem, 0, rem.size()); + } + + // all items which are left in the queue are intermediate results, so we must delete them + for (size_t i = 0; i < rem.size(); i++) { + delete rem[i]; + } + doneCallback(r); + + delete this; + } + + void AsyncAggregateAndPushAggQueue(std::shared_ptr >& vec, size_t start, size_t count, bool del) + { + IncWait(); + PushWork(std::bind(&Aggregator::SyncAggregateAndPushAggQueue, this, vec, start, count, del)); + } + + void SyncAggregateAndPushAggQueue(std::shared_ptr >& vec, size_t start, size_t count, bool del) + { + // aggregate vec and push the intermediate result onto the work queue + PushAggQueue(SyncAggregate(*vec, start, count)); + if (del) { + for (size_t i = 0; i < count; i++) { + delete (*vec)[start + i]; + } + } + CheckDone(); + } + + void PushAggQueue(const T& v) + { + aggQueue.push(new T(v)); + + if (++aggQueueSize >= batchSize) { + // we've collected enough intermediate results to form a new batch. + std::shared_ptr > newBatch; + { + std::unique_lock l(m); + if (aggQueueSize < batchSize) { + // some other worker thread grabbed this batch + return; + } + newBatch = std::make_shared >(batchSize); + // collect items for new batch + for (size_t i = 0; i < batchSize; i++) { + T* p = nullptr; + bool s = aggQueue.pop(p); + assert(s); + (*newBatch)[i] = p; + } + aggQueueSize -= batchSize; + } + + // push new batch to work queue. del=true this time as these items are intermediate results and need to be deleted + // after aggregation is done + AsyncAggregateAndPushAggQueue(newBatch, 0, newBatch->size(), true); + } + } + + template + T SyncAggregate(const std::vector& vec, size_t start, size_t count) + { + T result = *vec[start]; + for (size_t j = 1; j < count; j++) { + result.AggregateInsecure(*vec[start + j]); + } + return result; + } + + template + void PushWork(Callable&& f) + { + workerPool.push(f); + } +}; + +// Aggregates multiple input vectors into a single output vector +// Inputs are in the following form: +// [ +// [a1, b1, c1, d1], +// [a2, b2, c2, d2], +// [a3, b3, c3, d3], +// [a4, b4, c4, d4], +// ] +// The result is in the following form: +// [ a1+a2+a3+a4, b1+b2+b3+b4, c1+c2+c3+c4, d1+d2+d3+d4] +// Same rules for the input vectors apply to the VectorAggregator as for the Aggregator (they must stay alive) +template +struct VectorAggregator { + typedef Aggregator AggregatorType; + typedef std::vector VectorType; + typedef std::shared_ptr VectorPtrType; + typedef std::vector VectorVectorType; + typedef std::function DoneCallback; + DoneCallback doneCallback; + + const VectorVectorType& vecs; + size_t start; + size_t count; + bool parallel; + ctpl::thread_pool& workerPool; + + std::atomic doneCount; + + VectorPtrType result; + size_t vecSize; + + VectorAggregator(const VectorVectorType& _vecs, + size_t _start, size_t _count, + bool _parallel, ctpl::thread_pool& _workerPool, + DoneCallback _doneCallback) : + vecs(_vecs), + parallel(_parallel), + start(_start), + count(_count), + workerPool(_workerPool), + doneCallback(std::move(_doneCallback)) + { + assert(!vecs.empty()); + vecSize = vecs[0]->size(); + result = std::make_shared(vecSize); + doneCount = 0; + } + + void Start() + { + std::vector aggregators; + for (size_t i = 0; i < vecSize; i++) { + std::vector tmp(count); + for (size_t j = 0; j < count; j++) { + tmp[j] = &(*vecs[start + j])[i]; + } + + auto aggregator = new AggregatorType(std::move(tmp), 0, count, parallel, workerPool, std::bind(&VectorAggregator::CheckDone, this, std::placeholders::_1, i)); + // we can't directly start the aggregator here as it might be so fast that it deletes "this" while we are still in this loop + aggregators.emplace_back(aggregator); + } + for (auto agg : aggregators) { + agg->Start(); + } + } + + void CheckDone(const T& agg, size_t idx) + { + (*result)[idx] = agg; + if (++doneCount == vecSize) { + doneCallback(result); + delete this; + } + } +}; + +// See comment of AsyncVerifyContributionShares for a description on what this does +// Same rules as in Aggregator apply for the inputs +struct ContributionVerifier { + struct BatchState { + size_t start; + size_t count; + + BLSVerificationVectorPtr vvec; + CBLSSecretKey skShare; + + // starts with 0 and is incremented if either vvec or skShare aggregation finishs. If it reaches 2, we know + // that aggregation for this batch is fully done. We can then start verification. + std::unique_ptr > aggDone; + + // we can't directly update a vector in parallel + // as vector is not thread safe (uses bitsets internally) + // so we must use vector temporarely and concatenate/convert + // each batch result into a final vector + std::vector verifyResults; + }; + + CBLSId forId; + const std::vector& vvecs; + const BLSSecretKeyVector& skShares; + size_t batchSize; + bool parallel; + bool aggregated; + + ctpl::thread_pool& workerPool; + + size_t batchCount; + size_t verifyCount; + + std::vector batchStates; + std::atomic verifyDoneCount{0}; + std::function&)> doneCallback; + + ContributionVerifier(const CBLSId& _forId, const std::vector& _vvecs, + const BLSSecretKeyVector& _skShares, size_t _batchSize, + bool _parallel, bool _aggregated, ctpl::thread_pool& _workerPool, + std::function&)> _doneCallback) : + forId(_forId), + vvecs(_vvecs), + skShares(_skShares), + batchSize(_batchSize), + parallel(_parallel), + aggregated(_aggregated), + workerPool(_workerPool), + doneCallback(std::move(_doneCallback)) + { + } + + void Start() + { + if (!aggregated) { + // treat all inputs as one large batch + batchSize = vvecs.size(); + batchCount = 1; + } else { + batchCount = (vvecs.size() + batchSize - 1) / batchSize; + } + verifyCount = vvecs.size(); + + batchStates.resize(batchCount); + for (size_t i = 0; i < batchCount; i++) { + auto& batchState = batchStates[i]; + + batchState.aggDone.reset(new std::atomic(0)); + batchState.start = i * batchSize; + batchState.count = std::min(batchSize, vvecs.size() - batchState.start); + batchState.verifyResults.assign(batchState.count, 0); + } + + if (aggregated) { + size_t batchCount2 = batchCount; // 'this' might get deleted while we're still looping + for (size_t i = 0; i < batchCount2; i++) { + AsyncAggregate(i); + } + } else { + // treat all inputs as a single batch and verify one-by-one + AsyncVerifyBatchOneByOne(0); + } + } + + void Finish() + { + size_t batchIdx = 0; + std::vector result(vvecs.size()); + for (size_t i = 0; i < vvecs.size(); i += batchSize) { + auto& batchState = batchStates[batchIdx++]; + for (size_t j = 0; j < batchState.count; j++) { + result[batchState.start + j] = batchState.verifyResults[j] != 0; + } + } + doneCallback(result); + delete this; + } + + void AsyncAggregate(size_t batchIdx) + { + auto& batchState = batchStates[batchIdx]; + + // aggregate vvecs and skShares of batch in parallel + auto vvecAgg = new VectorAggregator(vvecs, batchState.start, batchState.count, parallel, workerPool, std::bind(&ContributionVerifier::HandleAggVvecDone, this, batchIdx, std::placeholders::_1)); + auto skShareAgg = new Aggregator(skShares, batchState.start, batchState.count, parallel, workerPool, std::bind(&ContributionVerifier::HandleAggSkShareDone, this, batchIdx, std::placeholders::_1)); + + vvecAgg->Start(); + skShareAgg->Start(); + } + + void HandleAggVvecDone(size_t batchIdx, const BLSVerificationVectorPtr& vvec) + { + auto& batchState = batchStates[batchIdx]; + batchState.vvec = vvec; + if (++(*batchState.aggDone) == 2) { + HandleAggDone(batchIdx); + } + } + void HandleAggSkShareDone(size_t batchIdx, const CBLSSecretKey& skShare) + { + auto& batchState = batchStates[batchIdx]; + batchState.skShare = skShare; + if (++(*batchState.aggDone) == 2) { + HandleAggDone(batchIdx); + } + } + + void HandleVerifyDone(size_t batchIdx, size_t count) + { + size_t c = verifyDoneCount += count; + if (c == verifyCount) { + Finish(); + } + } + + void HandleAggDone(size_t batchIdx) + { + auto& batchState = batchStates[batchIdx]; + + if (batchState.vvec == nullptr || batchState.vvec->empty() || !batchState.skShare.IsValid()) { + // something went wrong while aggregating and there is nothing we can do now except mark the whole batch as failed + // this can only happen if inputs were invalid in some way + batchState.verifyResults.assign(batchState.count, 0); + HandleVerifyDone(batchIdx, batchState.count); + return; + } + + AsyncAggregatedVerifyBatch(batchIdx); + } + + void AsyncAggregatedVerifyBatch(size_t batchIdx) + { + auto f = [this, batchIdx](int threadId) { + auto& batchState = batchStates[batchIdx]; + bool result = Verify(batchState.vvec, batchState.skShare); + if (result) { + // whole batch is valid + batchState.verifyResults.assign(batchState.count, 1); + HandleVerifyDone(batchIdx, batchState.count); + } else { + // at least one entry in the batch is invalid, revert to per-contribution verification (but parallelized) + AsyncVerifyBatchOneByOne(batchIdx); + } + }; + PushOrDoWork(std::move(f)); + } + + void AsyncVerifyBatchOneByOne(size_t batchIdx) + { + size_t count = batchStates[batchIdx].count; + batchStates[batchIdx].verifyResults.assign(count, 0); + for (size_t i = 0; i < count; i++) { + auto f = [this, i, batchIdx](int threadId) { + auto& batchState = batchStates[batchIdx]; + batchState.verifyResults[i] = Verify(vvecs[batchState.start + i], skShares[batchState.start + i]); + HandleVerifyDone(batchIdx, 1); + }; + PushOrDoWork(std::move(f)); + } + } + + bool Verify(const BLSVerificationVectorPtr& vvec, const CBLSSecretKey& skShare) + { + CBLSPublicKey pk1; + if (!pk1.PublicKeyShare(*vvec, forId)) { + return false; + } + + CBLSPublicKey pk2 = skShare.GetPublicKey(); + return pk1 == pk2; + } + + template + void PushOrDoWork(Callable&& f) + { + if (parallel) { + workerPool.push(std::move(f)); + } else { + f(0); + } + } +}; + +void CBLSWorker::AsyncBuildQuorumVerificationVector(const std::vector& vvecs, + size_t start, size_t count, bool parallel, + std::function doneCallback) +{ + if (start == 0 && count == 0) { + count = vvecs.size(); + } + if (vvecs.empty() || count == 0 || start > vvecs.size() || start + count > vvecs.size()) { + doneCallback(nullptr); + return; + } + if (!VerifyVerificationVectors(vvecs, start, count)) { + doneCallback(nullptr); + return; + } + + auto agg = new VectorAggregator(vvecs, start, count, parallel, workerPool, std::move(doneCallback)); + agg->Start(); +} + +std::future CBLSWorker::AsyncBuildQuorumVerificationVector(const std::vector& vvecs, + size_t start, size_t count, bool parallel) +{ + auto p = BuildFutureDoneCallback(); + AsyncBuildQuorumVerificationVector(vvecs, start, count, parallel, std::move(p.first)); + return std::move(p.second); +} + +BLSVerificationVectorPtr CBLSWorker::BuildQuorumVerificationVector(const std::vector& vvecs, + size_t start, size_t count, bool parallel) +{ + return AsyncBuildQuorumVerificationVector(vvecs, start, count, parallel).get(); +} + +template +void AsyncAggregateHelper(ctpl::thread_pool& workerPool, + const std::vector& vec, size_t start, size_t count, bool parallel, + std::function doneCallback) +{ + if (start == 0 && count == 0) { + count = vec.size(); + } + if (vec.empty() || count == 0 || start > vec.size() || start + count > vec.size()) { + doneCallback(T()); + return; + } + if (!VerifyVectorHelper(vec, start, count)) { + doneCallback(T()); + return; + } + + auto agg = new Aggregator(vec, start, count, parallel, workerPool, std::move(doneCallback)); + agg->Start(); +} + +void CBLSWorker::AsyncAggregateSecretKeys(const BLSSecretKeyVector& secKeys, + size_t start, size_t count, bool parallel, + std::function doneCallback) +{ + AsyncAggregateHelper(workerPool, secKeys, start, count, parallel, doneCallback); +} + +std::future CBLSWorker::AsyncAggregateSecretKeys(const BLSSecretKeyVector& secKeys, + size_t start, size_t count, bool parallel) +{ + auto p = BuildFutureDoneCallback(); + AsyncAggregateSecretKeys(secKeys, start, count, parallel, std::move(p.first)); + return std::move(p.second); +} + +CBLSSecretKey CBLSWorker::AggregateSecretKeys(const BLSSecretKeyVector& secKeys, + size_t start, size_t count, bool parallel) +{ + return AsyncAggregateSecretKeys(secKeys, start, count, parallel).get(); +} + +void CBLSWorker::AsyncAggregatePublicKeys(const BLSPublicKeyVector& pubKeys, + size_t start, size_t count, bool parallel, + std::function doneCallback) +{ + AsyncAggregateHelper(workerPool, pubKeys, start, count, parallel, doneCallback); +} + +std::future CBLSWorker::AsyncAggregatePublicKeys(const BLSPublicKeyVector& pubKeys, + size_t start, size_t count, bool parallel) +{ + auto p = BuildFutureDoneCallback(); + AsyncAggregatePublicKeys(pubKeys, start, count, parallel, std::move(p.first)); + return std::move(p.second); +} + +CBLSPublicKey CBLSWorker::AggregatePublicKeys(const BLSPublicKeyVector& pubKeys, + size_t start, size_t count, bool parallel) +{ + return AsyncAggregatePublicKeys(pubKeys, start, count, parallel).get(); +} + +void CBLSWorker::AsyncAggregateSigs(const BLSSignatureVector& sigs, + size_t start, size_t count, bool parallel, + std::function doneCallback) +{ + AsyncAggregateHelper(workerPool, sigs, start, count, parallel, doneCallback); +} + +std::future CBLSWorker::AsyncAggregateSigs(const BLSSignatureVector& sigs, + size_t start, size_t count, bool parallel) +{ + auto p = BuildFutureDoneCallback(); + AsyncAggregateSigs(sigs, start, count, parallel, std::move(p.first)); + return std::move(p.second); +} + +CBLSSignature CBLSWorker::AggregateSigs(const BLSSignatureVector& sigs, + size_t start, size_t count, bool parallel) +{ + return AsyncAggregateSigs(sigs, start, count, parallel).get(); +} + + +CBLSPublicKey CBLSWorker::BuildPubKeyShare(const BLSVerificationVectorPtr& vvec, const CBLSId& id) +{ + CBLSPublicKey pkShare; + pkShare.PublicKeyShare(*vvec, id); + return pkShare; +} + +void CBLSWorker::AsyncVerifyContributionShares(const CBLSId& forId, const std::vector& vvecs, const BLSSecretKeyVector& skShares, + bool parallel, bool aggregated, std::function&)> doneCallback) +{ + if (!forId.IsValid() || !VerifyVerificationVectors(vvecs)) { + std::vector result; + result.assign(vvecs.size(), false); + doneCallback(result); + return; + } + + auto verifier = new ContributionVerifier(forId, vvecs, skShares, 8, parallel, aggregated, workerPool, std::move(doneCallback)); + verifier->Start(); +} + +std::future > CBLSWorker::AsyncVerifyContributionShares(const CBLSId& forId, const std::vector& vvecs, const BLSSecretKeyVector& skShares, + bool parallel, bool aggregated) +{ + auto p = BuildFutureDoneCallback >(); + AsyncVerifyContributionShares(forId, vvecs, skShares, parallel, aggregated, std::move(p.first)); + return std::move(p.second); +} + +std::vector CBLSWorker::VerifyContributionShares(const CBLSId& forId, const std::vector& vvecs, const BLSSecretKeyVector& skShares, + bool parallel, bool aggregated) +{ + return AsyncVerifyContributionShares(forId, vvecs, skShares, parallel, aggregated).get(); +} + +std::future CBLSWorker::AsyncVerifyContributionShare(const CBLSId& forId, + const BLSVerificationVectorPtr& vvec, + const CBLSSecretKey& skContribution) +{ + if (!forId.IsValid() || !VerifyVerificationVector(*vvec)) { + auto p = BuildFutureDoneCallback(); + p.first(false); + return std::move(p.second); + } + + auto f = [this, &forId, &vvec, &skContribution](int threadId) { + CBLSPublicKey pk1; + if (!pk1.PublicKeyShare(*vvec, forId)) { + return false; + } + + CBLSPublicKey pk2 = skContribution.GetPublicKey(); + return pk1 == pk2; + }; + return workerPool.push(f); +} + +bool CBLSWorker::VerifyContributionShare(const CBLSId& forId, const BLSVerificationVectorPtr& vvec, + const CBLSSecretKey& skContribution) +{ + CBLSPublicKey pk1; + if (!pk1.PublicKeyShare(*vvec, forId)) { + return false; + } + + CBLSPublicKey pk2 = skContribution.GetPublicKey(); + return pk1 == pk2; +} + +bool CBLSWorker::VerifyVerificationVector(const BLSVerificationVector& vvec, size_t start, size_t count) +{ + return VerifyVectorHelper(vvec, start, count); +} + +bool CBLSWorker::VerifyVerificationVectors(const std::vector& vvecs, + size_t start, size_t count) +{ + if (start == 0 && count == 0) { + count = vvecs.size(); + } + + std::set set; + for (size_t i = 0; i < count; i++) { + auto& vvec = vvecs[start + i]; + if (vvec == nullptr) { + return false; + } + if (vvec->size() != vvecs[start]->size()) { + return false; + } + for (size_t j = 0; j < vvec->size(); j++) { + if (!(*vvec)[j].IsValid()) { + return false; + } + // check duplicates + if (!set.emplace((*vvec)[j].GetHash()).second) { + return false; + } + } + } + + return true; +} + +bool CBLSWorker::VerifySecretKeyVector(const BLSSecretKeyVector& secKeys, size_t start, size_t count) +{ + return VerifyVectorHelper(secKeys, start, count); +} + +bool CBLSWorker::VerifySignatureVector(const BLSSignatureVector& sigs, size_t start, size_t count) +{ + return VerifyVectorHelper(sigs, start, count); +} + +void CBLSWorker::AsyncSign(const CBLSSecretKey& secKey, const uint256& msgHash, CBLSWorker::SignDoneCallback doneCallback) +{ + workerPool.push([secKey, msgHash, doneCallback](int threadId) { + doneCallback(secKey.Sign(msgHash)); + }); +} + +std::future CBLSWorker::AsyncSign(const CBLSSecretKey& secKey, const uint256& msgHash) +{ + auto p = BuildFutureDoneCallback(); + AsyncSign(secKey, msgHash, std::move(p.first)); + return std::move(p.second); +} + +void CBLSWorker::AsyncVerifySig(const CBLSSignature& sig, const CBLSPublicKey& pubKey, const uint256& msgHash, + CBLSWorker::SigVerifyDoneCallback doneCallback, CancelCond cancelCond) +{ + if (!sig.IsValid() || !pubKey.IsValid()) { + doneCallback(false); + return; + } + + std::unique_lock l(sigVerifyMutex); + + bool foundDuplicate = false; + for (auto& s : sigVerifyQueue) { + if (s.msgHash == msgHash) { + foundDuplicate = true; + break; + } + } + + if (foundDuplicate) { + // batched/aggregated verification does not allow duplicate hashes, so we push what we currently have and start + // with a fresh batch + PushSigVerifyBatch(); + } + + sigVerifyQueue.emplace_back(std::move(doneCallback), std::move(cancelCond), sig, pubKey, msgHash); + if (sigVerifyBatchesInProgress == 0 || sigVerifyQueue.size() >= SIG_VERIFY_BATCH_SIZE) { + PushSigVerifyBatch(); + } +} + +std::future CBLSWorker::AsyncVerifySig(const CBLSSignature& sig, const CBLSPublicKey& pubKey, const uint256& msgHash, CancelCond cancelCond) +{ + auto p = BuildFutureDoneCallback2(); + AsyncVerifySig(sig, pubKey, msgHash, std::move(p.first), cancelCond); + return std::move(p.second); +} + +bool CBLSWorker::IsAsyncVerifyInProgress() +{ + std::unique_lock l(sigVerifyMutex); + return sigVerifyBatchesInProgress != 0; +} + +// sigVerifyMutex must be held while calling +void CBLSWorker::PushSigVerifyBatch() +{ + auto f = [this](int threadId, std::shared_ptr > _jobs) { + auto& jobs = *_jobs; + if (jobs.size() == 1) { + auto& job = jobs[0]; + if (!job.cancelCond()) { + bool valid = job.sig.VerifyInsecure(job.pubKey, job.msgHash); + job.doneCallback(valid); + } + std::unique_lock l(sigVerifyMutex); + sigVerifyBatchesInProgress--; + if (!sigVerifyQueue.empty()) { + PushSigVerifyBatch(); + } + return; + } + + CBLSSignature aggSig; + std::vector indexes; + std::vector pubKeys; + std::vector msgHashes; + indexes.reserve(jobs.size()); + pubKeys.reserve(jobs.size()); + msgHashes.reserve(jobs.size()); + for (size_t i = 0; i < jobs.size(); i++) { + auto& job = jobs[i]; + if (job.cancelCond()) { + continue; + } + if (pubKeys.empty()) { + aggSig = job.sig; + } else { + aggSig.AggregateInsecure(job.sig); + } + indexes.emplace_back(i); + pubKeys.emplace_back(job.pubKey); + msgHashes.emplace_back(job.msgHash); + } + + if (!pubKeys.empty()) { + bool allValid = aggSig.VerifyInsecureAggregated(pubKeys, msgHashes); + if (allValid) { + for (size_t i = 0; i < pubKeys.size(); i++) { + jobs[indexes[i]].doneCallback(true); + } + } else { + // one or more sigs were not valid, revert to per-sig verification + // TODO this could be improved if we would cache pairing results in some way as the previous aggregated verification already calculated all the pairings for the hashes + for (size_t i = 0; i < pubKeys.size(); i++) { + auto& job = jobs[indexes[i]]; + bool valid = job.sig.VerifyInsecure(job.pubKey, job.msgHash); + job.doneCallback(valid); + } + } + } + + std::unique_lock l(sigVerifyMutex); + sigVerifyBatchesInProgress--; + if (!sigVerifyQueue.empty()) { + PushSigVerifyBatch(); + } + }; + + auto batch = std::make_shared >(std::move(sigVerifyQueue)); + sigVerifyQueue.reserve(SIG_VERIFY_BATCH_SIZE); + + sigVerifyBatchesInProgress++; + workerPool.push(f, batch); +} diff --git a/src/bls/bls_worker.h b/src/bls/bls_worker.h new file mode 100644 index 000000000000..17621d6ded32 --- /dev/null +++ b/src/bls/bls_worker.h @@ -0,0 +1,204 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ABSOLUTE_CRYPTO_BLS_WORKER_H +#define ABSOLUTE_CRYPTO_BLS_WORKER_H + +#include "bls.h" + +#include "ctpl.h" + +#include +#include + +#include + +// Low level BLS/DKG stuff. All very compute intensive and optimized for parallelization +// The worker tries to parallelize as much as possible and utilizes a few properties of BLS aggregation to speed up things +// For example, public key vectors can be aggregated in parallel if they are split into batches and the batched aggregations are +// aggregated to a final public key. This utilizes that when aggregating keys (a+b+c+d) gives the same result as (a+b)+(c+d) +class CBLSWorker +{ +public: + typedef std::function SignDoneCallback; + typedef std::function SigVerifyDoneCallback; + typedef std::function CancelCond; + +private: + ctpl::thread_pool workerPool; + + static const int SIG_VERIFY_BATCH_SIZE = 8; + struct SigVerifyJob { + SigVerifyDoneCallback doneCallback; + CancelCond cancelCond; + CBLSSignature sig; + CBLSPublicKey pubKey; + uint256 msgHash; + SigVerifyJob(SigVerifyDoneCallback&& _doneCallback, CancelCond&& _cancelCond, const CBLSSignature& _sig, const CBLSPublicKey& _pubKey, const uint256& _msgHash) : + doneCallback(_doneCallback), + cancelCond(_cancelCond), + sig(_sig), + pubKey(_pubKey), + msgHash(_msgHash) + { + } + }; + + std::mutex sigVerifyMutex; + int sigVerifyBatchesInProgress{0}; + std::vector sigVerifyQueue; + +public: + CBLSWorker(); + ~CBLSWorker(); + + void Stop(); + + bool GenerateContributions(int threshold, const BLSIdVector& ids, BLSVerificationVectorPtr& vvecRet, BLSSecretKeyVector& skShares); + + // The following functions are all used to aggregate verification (public key) vectors + // Inputs are in the following form: + // [ + // [a1, b1, c1, d1], + // [a2, b2, c2, d2], + // [a3, b3, c3, d3], + // [a4, b4, c4, d4], + // ] + // The result is in the following form: + // [ a1+a2+a3+a4, b1+b2+b3+b4, c1+c2+c3+c4, d1+d2+d3+d4] + // Multiple things can be parallelized here. For example, all 4 entries in the result vector can be calculated in parallel + // Also, each individual vector can be split into multiple batches and aggregating the batches can also be paralellized. + void AsyncBuildQuorumVerificationVector(const std::vector& vvecs, + size_t start, size_t count, bool parallel, + std::function doneCallback); + std::future AsyncBuildQuorumVerificationVector(const std::vector& vvecs, + size_t start, size_t count, bool parallel); + BLSVerificationVectorPtr BuildQuorumVerificationVector(const std::vector& vvecs, + size_t start = 0, size_t count = 0, bool parallel = true); + + // The following functions are all used to aggregate single vectors + // Inputs are in the following form: + // [a, b, c, d], + // The result is simply a+b+c+d + // Aggregation is paralellized by splitting up the input vector into multiple batches and then aggregating the individual batch results + void AsyncAggregateSecretKeys(const BLSSecretKeyVector& secKeys, + size_t start, size_t count, bool parallel, + std::function doneCallback); + std::future AsyncAggregateSecretKeys(const BLSSecretKeyVector& secKeys, + size_t start, size_t count, bool parallel); + CBLSSecretKey AggregateSecretKeys(const BLSSecretKeyVector& secKeys, size_t start = 0, size_t count = 0, bool parallel = true); + + void AsyncAggregatePublicKeys(const BLSPublicKeyVector& pubKeys, + size_t start, size_t count, bool parallel, + std::function doneCallback); + std::future AsyncAggregatePublicKeys(const BLSPublicKeyVector& pubKeys, + size_t start, size_t count, bool parallel); + CBLSPublicKey AggregatePublicKeys(const BLSPublicKeyVector& pubKeys, size_t start = 0, size_t count = 0, bool parallel = true); + + void AsyncAggregateSigs(const BLSSignatureVector& sigs, + size_t start, size_t count, bool parallel, + std::function doneCallback); + std::future AsyncAggregateSigs(const BLSSignatureVector& sigs, + size_t start, size_t count, bool parallel); + CBLSSignature AggregateSigs(const BLSSignatureVector& sigs, size_t start = 0, size_t count = 0, bool parallel = true); + + + // Calculate public key share from public key vector and id. Not parallelized + CBLSPublicKey BuildPubKeyShare(const BLSVerificationVectorPtr& vvec, const CBLSId& id); + + // The following functions verify multiple verification vectors and contributions for the same id + // This is parallelized by performing batched verification. The verification vectors and the contributions of + // a batch are aggregated (in parallel, see AsyncBuildQuorumVerificationVector and AsyncBuildSecretKeyShare). The + // result per batch is a single aggregated verification vector and a single aggregated contribution, which are then + // verified with VerifyContributionShare. If verification of the aggregated inputs is successful, the whole batch + // is marked as valid. If the batch verification fails, the individual entries are verified in a non-aggregated manner + void AsyncVerifyContributionShares(const CBLSId& forId, const std::vector& vvecs, const BLSSecretKeyVector& skShares, + bool parallel, bool aggregated, std::function&)> doneCallback); + std::future > AsyncVerifyContributionShares(const CBLSId& forId, const std::vector& vvecs, const BLSSecretKeyVector& skShares, + bool parallel, bool aggregated); + std::vector VerifyContributionShares(const CBLSId& forId, const std::vector& vvecs, const BLSSecretKeyVector& skShares, + bool parallel = true, bool aggregated = true); + + std::future AsyncVerifyContributionShare(const CBLSId& forId, const BLSVerificationVectorPtr& vvec, const CBLSSecretKey& skContribution); + + // Non paralellized verification of a single contribution + bool VerifyContributionShare(const CBLSId& forId, const BLSVerificationVectorPtr& vvec, const CBLSSecretKey& skContribution); + + // Simple verification of vectors. Checks x.IsValid() for every entry and checks for duplicate entries + bool VerifyVerificationVector(const BLSVerificationVector& vvec, size_t start = 0, size_t count = 0); + bool VerifyVerificationVectors(const std::vector& vvecs, size_t start = 0, size_t count = 0); + bool VerifySecretKeyVector(const BLSSecretKeyVector& secKeys, size_t start = 0, size_t count = 0); + bool VerifySignatureVector(const BLSSignatureVector& sigs, size_t start = 0, size_t count = 0); + + // Internally batched signature signing and verification + void AsyncSign(const CBLSSecretKey& secKey, const uint256& msgHash, SignDoneCallback doneCallback); + std::future AsyncSign(const CBLSSecretKey& secKey, const uint256& msgHash); + void AsyncVerifySig(const CBLSSignature& sig, const CBLSPublicKey& pubKey, const uint256& msgHash, SigVerifyDoneCallback doneCallback, CancelCond cancelCond = [] { return false; }); + std::future AsyncVerifySig(const CBLSSignature& sig, const CBLSPublicKey& pubKey, const uint256& msgHash, CancelCond cancelCond = [] { return false; }); + bool IsAsyncVerifyInProgress(); + +private: + void PushSigVerifyBatch(); +}; + +// Builds and caches different things from CBLSWorker +// Cache keys are provided externally as computing hashes on BLS vectors is too expensive +// If multiple threads try to build the same thing at the same time, only one will actually build it +// and the other ones will wait for the result of the first caller +class CBLSWorkerCache +{ +private: + CBLSWorker& worker; + + std::mutex cacheCs; + std::map > vvecCache; + std::map > secretKeyShareCache; + std::map > publicKeyShareCache; + +public: + CBLSWorkerCache(CBLSWorker& _worker) : + worker(_worker) {} + + BLSVerificationVectorPtr BuildQuorumVerificationVector(const uint256& cacheKey, const std::vector& vvecs) + { + return GetOrBuild(cacheKey, vvecCache, [&]() { + return worker.BuildQuorumVerificationVector(vvecs); + }); + } + CBLSSecretKey AggregateSecretKeys(const uint256& cacheKey, const BLSSecretKeyVector& skShares) + { + return GetOrBuild(cacheKey, secretKeyShareCache, [&]() { + return worker.AggregateSecretKeys(skShares); + }); + } + CBLSPublicKey BuildPubKeyShare(const uint256& cacheKey, const BLSVerificationVectorPtr& vvec, const CBLSId& id) + { + return GetOrBuild(cacheKey, publicKeyShareCache, [&]() { + return worker.BuildPubKeyShare(vvec, id); + }); + } + +private: + template + T GetOrBuild(const uint256& cacheKey, std::map >& cache, Builder&& builder) + { + cacheCs.lock(); + auto it = cache.find(cacheKey); + if (it != cache.end()) { + auto f = it->second; + cacheCs.unlock(); + return f.get(); + } + + std::promise p; + cache.emplace(cacheKey, p.get_future()); + cacheCs.unlock(); + + T v = builder(); + p.set_value(v); + return v; + } +}; + +#endif //ABSOLUTE_CRYPTO_BLS_WORKER_H diff --git a/src/cachemap.h b/src/cachemap.h index 61d8776b3812..3096c069e7cf 100644 --- a/src/cachemap.h +++ b/src/cachemap.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/cachemultimap.h b/src/cachemultimap.h index 05c7b8615a74..a1cd801523f9 100644 --- a/src/cachemultimap.h +++ b/src/cachemultimap.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 622e998b7b52..b731abecff18 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -38,7 +38,7 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi genesis.nBits = nBits; genesis.nNonce = nNonce; genesis.nVersion = nVersion; - + genesis.vtx.push_back(MakeTransactionRef(std::move(txNew))); genesis.hashPrevBlock.SetNull(); genesis.hashMerkleRoot = BlockMerkleRoot(genesis); @@ -151,8 +151,8 @@ class CMainParams : public CChainParams { consensus.nMasternodeMinimumConfirmations = 15; consensus.BIP34Height = 0; consensus.BIP34Hash = uint256S("0x00000de52875a68d7bf6a5bb5ad1b89fd7df4d67a9603669327949923dc74d7e"); - consensus.BIP65Height = 390000; - consensus.BIP66Height = 395000; + consensus.BIP65Height = 390000; + consensus.BIP66Height = 395000; consensus.DIP0001Height = 450000; consensus.powLimit = uint256S("00000fffff000000000000000000000000000000000000000000000000000000"); consensus.nPowTargetTimespan = 7.5 * 60; // 5 blocks @@ -185,6 +185,13 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 4032; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 3226; // 80% of 4032 + // Deployment of InstantSend autolocks + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nStartTime = 1533945600; // Aug 11th, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nTimeout = 1565481600; // Aug 11th, 2020 + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nWindowSize = 4032; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nThreshold = 3226; // 80% of 4032 + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000000100000"); @@ -220,7 +227,7 @@ class CMainParams : public CChainParams { base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,23); // Absolute script addresses start with 's' base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,125); - // Absolute private keys start with 'm' + // Absolute private keys start with 'h' base58Prefixes[SECRET_KEY] = std::vector(1,110); // Absolute BIP32 pubkeys start with 'xpub' (Bitcoin defaults) base58Prefixes[EXT_PUBLIC_KEY] = boost::assign::list_of(0x04)(0x88)(0xB2)(0x1E).convert_to_container >(); @@ -235,13 +242,16 @@ class CMainParams : public CChainParams { fMiningRequiresPeers = true; fDefaultConsistencyChecks = false; fRequireStandard = true; + fRequireRoutableExternalIP = true; fMineBlocksOnDemand = false; fAllowMultipleAddressesFromGroup = false; fAllowMultiplePorts = false; nPoolMaxTransactions = 3; nFulfilledRequestExpireTime = 60*60; // fulfilled requests expire in 1 hour - strSporkPubKey = "04ef78a6e3c69ce24e01f929bcfff56844320c78f840b4febcf21d634e3951d213c94c4d7830f6d8a987fd04ad82982efb85e471b763b13841bc93f427929302cf"; + + vSporkAddresses = {"AYZX23zmfNQgCZtzq7JsWHRKKyuzzxYHfD"}; + nMinSporkKeys = 1; checkpointData = (CCheckpointData) { boost::assign::map_list_of @@ -251,15 +261,21 @@ class CMainParams : public CChainParams { ( 250000, uint256S("0x00000000000640d8897addd4200209b6dd754aa3ca1f39857f8f03f36a44cc02")) ( 300000, uint256S("0x00000000000abec2c4fe6aec36d3f37026df8ed8817789a43278392092fe9d1a")) ( 350400, uint256S("0x0000000000056195689db2edb979305163b7ed88c1e7ce8875bd2b5feb4c432d")) - + ( 400000, uint256S("0x00000000000c0e8ce3c39cc777ad1e02b23ad5b4dc8eac6c3ea0cd0e742ce2bf")) + ( 450000, uint256S("0x0000000001abd905352d4394804c249add2842fb4f4ff72c5d4bce3c16644c0a")) + ( 500000, uint256S("0x000000000c65d825e79473571c04e3de2c94d9c0c9abbbc78b8849c2e6bcf48e")) + ( 550000, uint256S("0x00000000003b29d12245a3347521ecfb8206a54846a3177f175d5d93c7bd2cc4")) + ( 600000, uint256S("0x00000000001398fddff3d3618ecc9c5a5821dfc9427602b4bacd0ef83e75e5c6")) + ( 650000, uint256S("0x00000000000509ff01583ea601b6e72cb2f0c68bd7b9583614da872b5a613c6a")) + }; chainTxData = ChainTxData{ - 2813478628, // * UNIX timestamp of last known number of transactions - 476506, // * total number of transactions between genesis and that timestamp + 1594068448, // * UNIX timestamp of last known number of transactions + 859693, // * total number of transactions between genesis and that timestamp // (the tx=... number in the SetBestChain debug.log lines) - 1 //estimated number of transactions per second after that timestamp + 0.05 //estimated number of transactions per second after that timestamp }; } }; @@ -319,10 +335,17 @@ class CTestNetParams : public CChainParams { // Deployment of BIP147 consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 1517792400; // Feb 5th, 2018 - consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 1549328400; // Feb 5th, 2019 + consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 1549328400; // Feb 5th, 2020 consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 50; // 50% of 100 + // Deployment of InstantSend autolocks + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nStartTime = 1532476800; // Jul 25th, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nTimeout = 1564012800; // Jul 25th, 2020 + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nWindowSize = 100; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nThreshold = 50; // 50% of 100 + // The best chain should have at least this much work. // not used currently consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000000000000000100000"); @@ -350,9 +373,11 @@ class CTestNetParams : public CChainParams { assert(genesis.hashMerkleRoot == uint256S("0x12844a9cbf517654e272975506ab56af4d5c8dde0332a0ee48ba159c72daae03")); vFixedSeeds.clear(); + vFixedSeeds = std::vector(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); + vSeeds.clear(); // nodes with support for servicebits filtering should be at the top - vSeeds.push_back(CDNSSeedData("absolutecoin.net", "tseed1.absolutecoin.net")); + vSeeds.push_back(CDNSSeedData("absolutecoin.net", "tseed1.absolutecoin.net")); vSeeds.push_back(CDNSSeedData("absolutecoin.net", "tseed2.absolutecoin.net")); vSeeds.push_back(CDNSSeedData("absolutecoin.net", "tseed3.absolutecoin.net")); @@ -374,13 +399,16 @@ class CTestNetParams : public CChainParams { fMiningRequiresPeers = false; fDefaultConsistencyChecks = false; fRequireStandard = false; + fRequireRoutableExternalIP = true; fMineBlocksOnDemand = true; fAllowMultipleAddressesFromGroup = false; fAllowMultiplePorts = false; nPoolMaxTransactions = 3; nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes - strSporkPubKey = "044cff76819f7fafc4fd1b2de299e7052cc60c137330485d1b0018e9f14fdebe3d1b0595f716851837655a61dbea5a2f80727d43b7aa7a598b0222e3f547eeefa2"; + + vSporkAddresses = {"yQzniXSTqRwaEujSkvZicJsYZkt7wpa4NJ"}; + nMinSporkKeys = 1; checkpointData = (CCheckpointData) { boost::assign::map_list_of @@ -452,10 +480,24 @@ class CPoVNETParams : public CChainParams { // Deployment of BIP147 consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 1517792400; // Feb 5th, 2018 - consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 1549328400; // Feb 5th, 2019 + consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 1549328400; // Feb 5th, 2020 consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 50; // 50% of 100 + // Deployment of AIP0003 + consensus.vDeployments[Consensus::DEPLOYMENT_AIP0003].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_AIP0003].nStartTime = 1556150400; // Thursday, 25-Apr-19 00:00:00 UTC + consensus.vDeployments[Consensus::DEPLOYMENT_AIP0003].nTimeout = 1587772800; // Saturday, 25-Apr-20 00:00:00 UTC + consensus.vDeployments[Consensus::DEPLOYMENT_AIP0003].nWindowSize = 100; + consensus.vDeployments[Consensus::DEPLOYMENT_AIP0003].nThreshold = 50; // 50% of 100 + + // Deployment of InstantSend autolocks + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nStartTime = 1535752800; // Sep 1st, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nTimeout = 1567288800; // Sep 1st, 2020 + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nWindowSize = 100; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nThreshold = 50; // 50% of 100 + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000"); @@ -505,7 +547,9 @@ class CPoVNETParams : public CChainParams { nPoolMaxTransactions = 3; nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes - strSporkPubKey = "046f78dcf911fbd61910136f7f0f8d90578f68d0b3ac973b5040fb7afb501b5939f39b108b0569dca71488f5bbf498d92e4d1194f6f941307ffd95f75e76869f0e"; + + vSporkAddresses = {"yQzniXSTqRwaEujSkvZicJsYZkt7wpa4NJ"}; + nMinSporkKeys = 1; checkpointData = (CCheckpointData) { boost::assign::map_list_of @@ -557,7 +601,7 @@ class CRegTestParams : public CChainParams { consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = true; - consensus.nPowDGWHeight = 25; + consensus.nPowDGWHeight = 25; consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; @@ -572,6 +616,13 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_AIP0003].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_AIP0003].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_AIP0003].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_ISAUTOLOCKS].nTimeout = 999999999999ULL; + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); @@ -601,12 +652,17 @@ class CRegTestParams : public CChainParams { fMiningRequiresPeers = false; fDefaultConsistencyChecks = true; fRequireStandard = false; + fRequireRoutableExternalIP = false; fMineBlocksOnDemand = true; fAllowMultipleAddressesFromGroup = true; fAllowMultiplePorts = true; nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes + // privKey: cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK + vSporkAddresses = {"ydUCk8yb3HJ9BsUjfi6snQxHKw3cEDCiNW"}; + nMinSporkKeys = 1; + checkpointData = (CCheckpointData){ boost::assign::map_list_of ( 0, uint256S("0x00000d8cfc345deda15f4897bb9ed59878fb4c84f02c478dc1165ee8fbaede56")) diff --git a/src/chainparams.h b/src/chainparams.h index 2b9a27cd180b..794ae3ad8959 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -69,6 +69,8 @@ class CChainParams bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; } /** Policy: Filter transactions that do not match well-defined patterns */ bool RequireStandard() const { return fRequireStandard; } + /** Require addresses specified with "-externalip" parameter to be routable */ + bool RequireRoutableExternalIP() const { return fRequireRoutableExternalIP; } uint64_t PruneAfterHeight() const { return nPruneAfterHeight; } /** Make miner stop after a block is found. In RPC, don't return until nGenProcLimit blocks are generated */ bool MineBlocksOnDemand() const { return fMineBlocksOnDemand; } @@ -86,7 +88,8 @@ class CChainParams const ChainTxData& TxData() const { return chainTxData; } int PoolMaxTransactions() const { return nPoolMaxTransactions; } int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; } - std::string SporkPubKey() const { return strSporkPubKey; } + const std::vector& SporkAddresses() const { return vSporkAddresses; } + int MinSporkKeys() const { return nMinSporkKeys; } protected: CChainParams() {} @@ -106,6 +109,7 @@ class CChainParams bool fMiningRequiresPeers; bool fDefaultConsistencyChecks; bool fRequireStandard; + bool fRequireRoutableExternalIP; bool fMineBlocksOnDemand; bool fAllowMultipleAddressesFromGroup; bool fAllowMultiplePorts; @@ -113,8 +117,8 @@ class CChainParams ChainTxData chainTxData; int nPoolMaxTransactions; int nFulfilledRequestExpireTime; - std::string strSporkPubKey; - std::string strMasternodePaymentsPubKey; + std::vector vSporkAddresses; + int nMinSporkKeys; }; /** diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index a56a9e95056e..91488d3cfd1e 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -8,43 +8,11 @@ * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly. */ static SeedSpec6 pnSeed6_main[] = { - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0xca,0x01}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x29,0xf2}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x29,0xf1}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x33,0xff,0xae,0xee}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x36,0x25,0x0e,0xf0}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa4,0x84,0xc3,0x4f}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x97,0x50,0xe9,0x74}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x60,0xcb}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x28,0x9d}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x29,0x23}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x29,0xc6}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x2c,0x00}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x61,0xe1}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x69,0x45}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x63,0x71}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x63,0x6c}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x63,0x79}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x6a,0x4b}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x2c,0x4b}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x2c,0x4c}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0x2c,0x4d}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0xc7,0xfa}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xd7}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xdc}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xdd}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xde}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xdf}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xe0}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xe1}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xe2}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xe3}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x2e,0xd7,0xe4}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x45,0xd8,0x2f}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x45,0xd8,0x2e}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x45,0xd9,0x94}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0xaf,0x71,0x8c}, 18888}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x74,0xcb,0x35,0x5d}, 18888} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0xc6,0x5f,0xae}, 18888}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x87,0xb5,0x21,0x12}, 18888}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4d,0x8a,0xdb}, 18888}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xd8,0xd1,0x19}, 18888}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x74,0xcb,0xca,0x44}, 18888} }; static SeedSpec6 pnSeed6_test[] = { diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 3e52862c1785..06175e06fbbe 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2014 The Bitcoin developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/clientversion.h b/src/clientversion.h index 1adb73bb1253..a82e8443960d 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -16,8 +16,8 @@ //! These need to be macros, as clientversion.cpp's and absolute*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 #define CLIENT_VERSION_MINOR 12 -#define CLIENT_VERSION_REVISION 2 -#define CLIENT_VERSION_BUILD 5 +#define CLIENT_VERSION_REVISION 3 +#define CLIENT_VERSION_BUILD 1 //! Set to true for release, false for prerelease or test build @@ -27,7 +27,7 @@ * Copyright year (2009-this) * Todo: update this when changing our copyright comments in the source */ -#define COPYRIGHT_YEAR 2019 +#define COPYRIGHT_YEAR 2020 #endif //HAVE_CONFIG_H diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index cd5b2f77fbf9..461f72ba5eca 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -7,6 +7,7 @@ #endif #include +#include #if defined(HAVE_SYS_SELECT_H) #include @@ -27,3 +28,47 @@ extern "C" FDELT_TYPE __fdelt_warn(FDELT_TYPE a) return a / __NFDBITS; } extern "C" FDELT_TYPE __fdelt_chk(FDELT_TYPE) __attribute__((weak, alias("__fdelt_warn"))); + +#if defined(__i386__) || defined(__arm__) + +extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp); + +extern "C" int64_t __wrap___divmoddi4(int64_t u, int64_t v, int64_t* rp) +{ + int32_t c1 = 0, c2 = 0; + int64_t uu = u, vv = v; + int64_t w; + int64_t r; + + if (uu < 0) { + c1 = ~c1, c2 = ~c2, uu = -uu; + } + if (vv < 0) { + c1 = ~c1, vv = -vv; + } + + w = __udivmoddi4(uu, vv, (uint64_t*)&r); + if (c1) + w = -w; + if (c2) + r = -r; + + *rp = r; + return w; +} +#endif + +extern "C" float log2f_old(float x); +#ifdef __i386__ +__asm(".symver log2f_old,log2f@GLIBC_2.1"); +#elif defined(__amd64__) +__asm(".symver log2f_old,log2f@GLIBC_2.2.5"); +#elif defined(__arm__) +__asm(".symver log2f_old,log2f@GLIBC_2.4"); +#elif defined(__aarch64__) +__asm(".symver log2f_old,log2f@GLIBC_2.17"); +#endif +extern "C" float __wrap_log2f(float x) +{ + return log2f_old(x); +} \ No newline at end of file diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index d53a551bb48d..46c4c147b8c1 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -18,6 +18,8 @@ inline unsigned int MaxBlockSigOps(bool fDIP0001Active /*= false */) { return MaxBlockSize(fDIP0001Active) / 50; } +/** The maximum allowed size of version 3 extra payload */ +static const unsigned int MAX_TX_EXTRA_PAYLOAD = 10000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ static const int COINBASE_MATURITY = 50; diff --git a/src/consensus/params.h b/src/consensus/params.h index 5dfda9a3467c..5d8d93b4c92d 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -18,6 +18,8 @@ enum DeploymentPos DEPLOYMENT_CSV, // Deployment of BIP68, BIP112, and BIP113. DEPLOYMENT_DIP0001, // Deployment of DIP0001 and lower transaction fees. DEPLOYMENT_BIP147, // Deployment of BIP147 (NULLDUMMY) + DEPLOYMENT_AIP0003, // Deployment of AIP0002 and AIP0003 (txv3 and deterministic MN lists) + DEPLOYMENT_ISAUTOLOCKS, // Deployment of automatic IS locks for simple transactions // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/crypto/Lyra2RE/Lyra2RE.c b/src/crypto/Lyra2RE/Lyra2RE.c index 4c0afc06ca3a..e252991bd7b0 100644 --- a/src/crypto/Lyra2RE/Lyra2RE.c +++ b/src/crypto/Lyra2RE/Lyra2RE.c @@ -40,36 +40,6 @@ #include "sph_skein.h" #include "Lyra2.h" -void lyra2re_hash(const char* input, char* output) -{ - sph_blake256_context ctx_blake; - sph_groestl256_context ctx_groestl; - sph_keccak256_context ctx_keccak; - sph_skein256_context ctx_skein; - - uint32_t hashA[8], hashB[8]; - - sph_blake256_init(&ctx_blake); - sph_blake256 (&ctx_blake, input, 80); - sph_blake256_close (&ctx_blake, hashA); - - sph_keccak256_init(&ctx_keccak); - sph_keccak256 (&ctx_keccak,hashA, 32); - sph_keccak256_close(&ctx_keccak, hashB); - - LYRA2_old(hashA, 32, hashB, 32, hashB, 32, 1, 8, 8); - - sph_skein256_init(&ctx_skein); - sph_skein256 (&ctx_skein, hashA, 32); - sph_skein256_close(&ctx_skein, hashB); - - sph_groestl256_init(&ctx_groestl); - sph_groestl256 (&ctx_groestl, hashB, 32); - sph_groestl256_close(&ctx_groestl, hashA); - - memcpy(output, hashA, 32); -} - void lyra2re2_hash(const char* input, char* output) { sph_blake256_context ctx_blake; diff --git a/src/crypto/Lyra2RE/Lyra2RE.h b/src/crypto/Lyra2RE/Lyra2RE.h index 77bcbe414bbb..467176d2a188 100644 --- a/src/crypto/Lyra2RE/Lyra2RE.h +++ b/src/crypto/Lyra2RE/Lyra2RE.h @@ -5,7 +5,6 @@ extern "C" { #endif -void lyra2re_hash(const char* input, char* output); void lyra2re2_hash(const char* input, char* output); #ifdef __cplusplus diff --git a/src/crypto/aes_helper.c b/src/crypto/Lyra2RE/aes_helper.c similarity index 100% rename from src/crypto/aes_helper.c rename to src/crypto/Lyra2RE/aes_helper.c diff --git a/src/crypto/Lyra2RE/bmw.c b/src/crypto/Lyra2RE/bmw.c index 718191d0e20b..b89a881e80c8 100644 --- a/src/crypto/Lyra2RE/bmw.c +++ b/src/crypto/Lyra2RE/bmw.c @@ -34,6 +34,10 @@ #include #include +#ifdef __cplusplus +extern "C"{ +#endif + #include "sph_bmw.h" #if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_BMW @@ -955,3 +959,7 @@ sph_bmw512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) } #endif + +#ifdef __cplusplus +} +#endif diff --git a/src/crypto/Lyra2RE/cubehash.c b/src/crypto/Lyra2RE/cubehash.c index f993c05b3523..9322fe14e055 100644 --- a/src/crypto/Lyra2RE/cubehash.c +++ b/src/crypto/Lyra2RE/cubehash.c @@ -35,6 +35,9 @@ #include #include "sph_cubehash.h" +#ifdef __cplusplus +extern "C"{ +#endif #if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_CUBEHASH #define SPH_SMALL_FOOTPRINT_CUBEHASH 1 @@ -715,3 +718,6 @@ sph_cubehash512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) cubehash_close(cc, ub, n, dst, 16); sph_cubehash512_init(cc); } +#ifdef __cplusplus +} +#endif diff --git a/src/crypto/echo.c b/src/crypto/Lyra2RE/echo.c similarity index 100% rename from src/crypto/echo.c rename to src/crypto/Lyra2RE/echo.c diff --git a/src/crypto/Lyra2RE/groestl.c b/src/crypto/Lyra2RE/groestl.c index aa0d1186ef42..928bc41efab9 100644 --- a/src/crypto/Lyra2RE/groestl.c +++ b/src/crypto/Lyra2RE/groestl.c @@ -2813,6 +2813,7 @@ static void groestl_small_close(sph_groestl_small_context *sc, unsigned ub, unsigned n, void *dst, size_t out_len) { + unsigned char *buf; unsigned char pad[72]; size_t u, ptr, pad_len; #if SPH_64 @@ -2823,6 +2824,7 @@ groestl_small_close(sph_groestl_small_context *sc, unsigned z; DECL_STATE_SMALL + buf = sc->buf; ptr = sc->ptr; z = 0x80 >> n; pad[0] = ((ub & -z) | z) & 0xFF; @@ -2947,6 +2949,7 @@ static void groestl_big_close(sph_groestl_big_context *sc, unsigned ub, unsigned n, void *dst, size_t out_len) { + unsigned char *buf; unsigned char pad[136]; size_t ptr, pad_len, u; #if SPH_64 @@ -2957,6 +2960,7 @@ groestl_big_close(sph_groestl_big_context *sc, unsigned z; DECL_STATE_BIG + buf = sc->buf; ptr = sc->ptr; z = 0x80 >> n; pad[0] = ((ub & -z) | z) & 0xFF; @@ -3116,4 +3120,4 @@ sph_groestl512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/src/crypto/jh.c b/src/crypto/Lyra2RE/jh.c similarity index 100% rename from src/crypto/jh.c rename to src/crypto/Lyra2RE/jh.c diff --git a/src/crypto/Lyra2RE/keccak.c b/src/crypto/Lyra2RE/keccak.c index cfd5916788f9..cff9f87dae68 100644 --- a/src/crypto/Lyra2RE/keccak.c +++ b/src/crypto/Lyra2RE/keccak.c @@ -1821,4 +1821,4 @@ sph_keccak512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/src/crypto/luffa.c b/src/crypto/Lyra2RE/luffa.c similarity index 100% rename from src/crypto/luffa.c rename to src/crypto/Lyra2RE/luffa.c diff --git a/src/crypto/shavite.c b/src/crypto/Lyra2RE/shavite.c similarity index 100% rename from src/crypto/shavite.c rename to src/crypto/Lyra2RE/shavite.c diff --git a/src/crypto/simd.c b/src/crypto/Lyra2RE/simd.c similarity index 100% rename from src/crypto/simd.c rename to src/crypto/Lyra2RE/simd.c diff --git a/src/crypto/Lyra2RE/sph_blake.h b/src/crypto/Lyra2RE/sph_blake.h index 1f8f8fa1521f..d8d794399d00 100644 --- a/src/crypto/Lyra2RE/sph_blake.h +++ b/src/crypto/Lyra2RE/sph_blake.h @@ -324,4 +324,4 @@ void sph_blake512_addbits_and_close( } #endif -#endif \ No newline at end of file +#endif diff --git a/src/crypto/Lyra2RE/sph_bmw.h b/src/crypto/Lyra2RE/sph_bmw.h index 484a2a745489..d386b0c14053 100644 --- a/src/crypto/Lyra2RE/sph_bmw.h +++ b/src/crypto/Lyra2RE/sph_bmw.h @@ -36,6 +36,10 @@ #ifndef SPH_BMW_H__ #define SPH_BMW_H__ +#ifdef __cplusplus +extern "C"{ +#endif + #include #include "sph_types.h" @@ -317,4 +321,8 @@ void sph_bmw512_addbits_and_close( #endif +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/crypto/Lyra2RE/sph_cubehash.h b/src/crypto/Lyra2RE/sph_cubehash.h index c66367480e12..487a1946ad40 100644 --- a/src/crypto/Lyra2RE/sph_cubehash.h +++ b/src/crypto/Lyra2RE/sph_cubehash.h @@ -37,6 +37,10 @@ #ifndef SPH_CUBEHASH_H__ #define SPH_CUBEHASH_H__ +#ifdef __cplusplus +extern "C"{ +#endif + #include #include "sph_types.h" @@ -281,5 +285,8 @@ void sph_cubehash512_close(void *cc, void *dst); */ void sph_cubehash512_addbits_and_close( void *cc, unsigned ub, unsigned n, void *dst); +#ifdef __cplusplus +} +#endif #endif diff --git a/src/crypto/sph_echo.h b/src/crypto/Lyra2RE/sph_echo.h similarity index 100% rename from src/crypto/sph_echo.h rename to src/crypto/Lyra2RE/sph_echo.h diff --git a/src/crypto/Lyra2RE/sph_groestl.h b/src/crypto/Lyra2RE/sph_groestl.h index 45b5923b8e35..495f05e21174 100644 --- a/src/crypto/Lyra2RE/sph_groestl.h +++ b/src/crypto/Lyra2RE/sph_groestl.h @@ -326,4 +326,4 @@ void sph_groestl512_addbits_and_close( } #endif -#endif \ No newline at end of file +#endif diff --git a/src/crypto/sph_jh.h b/src/crypto/Lyra2RE/sph_jh.h similarity index 100% rename from src/crypto/sph_jh.h rename to src/crypto/Lyra2RE/sph_jh.h diff --git a/src/crypto/Lyra2RE/sph_keccak.h b/src/crypto/Lyra2RE/sph_keccak.h index 4e9586b7d314..bdafdb88db02 100644 --- a/src/crypto/Lyra2RE/sph_keccak.h +++ b/src/crypto/Lyra2RE/sph_keccak.h @@ -290,4 +290,4 @@ void sph_keccak512_addbits_and_close( } #endif -#endif \ No newline at end of file +#endif diff --git a/src/crypto/sph_luffa.h b/src/crypto/Lyra2RE/sph_luffa.h similarity index 100% rename from src/crypto/sph_luffa.h rename to src/crypto/Lyra2RE/sph_luffa.h diff --git a/src/crypto/sph_shavite.h b/src/crypto/Lyra2RE/sph_shavite.h similarity index 100% rename from src/crypto/sph_shavite.h rename to src/crypto/Lyra2RE/sph_shavite.h diff --git a/src/crypto/sph_simd.h b/src/crypto/Lyra2RE/sph_simd.h similarity index 100% rename from src/crypto/sph_simd.h rename to src/crypto/Lyra2RE/sph_simd.h diff --git a/src/crypto/Lyra2RE/sph_skein.h b/src/crypto/Lyra2RE/sph_skein.h index e045956f6b9a..bddbc86fa5aa 100644 --- a/src/crypto/Lyra2RE/sph_skein.h +++ b/src/crypto/Lyra2RE/sph_skein.h @@ -295,4 +295,4 @@ void sph_skein512_addbits_and_close( } #endif -#endif \ No newline at end of file +#endif diff --git a/src/crypto/Lyra2RE/sph_types.h b/src/crypto/Lyra2RE/sph_types.h index 56a314cdb8f1..7295b0b37097 100644 --- a/src/crypto/Lyra2RE/sph_types.h +++ b/src/crypto/Lyra2RE/sph_types.h @@ -1973,4 +1973,4 @@ sph_dec64le_aligned(const void *src) #endif /* Doxygen excluded block */ -#endif \ No newline at end of file +#endif diff --git a/src/crypto/blake.c b/src/crypto/blake.c deleted file mode 100644 index 0650b9cf21e0..000000000000 --- a/src/crypto/blake.c +++ /dev/null @@ -1,1120 +0,0 @@ -/* $Id: blake.c 252 2011-06-07 17:55:14Z tp $ */ -/* - * BLAKE implementation. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @author Thomas Pornin - */ - -#include -#include -#include - -#include "sph_blake.h" - -#ifdef __cplusplus -extern "C"{ -#endif - -#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_BLAKE -#define SPH_SMALL_FOOTPRINT_BLAKE 1 -#endif - -#if SPH_SMALL_FOOTPRINT_BLAKE -#define SPH_COMPACT_BLAKE_32 1 -#endif - -#if SPH_64 && (SPH_SMALL_FOOTPRINT_BLAKE || !SPH_64_TRUE) -#define SPH_COMPACT_BLAKE_64 1 -#endif - -#ifdef _MSC_VER -#pragma warning (disable: 4146) -#endif - -static const sph_u32 IV224[8] = { - SPH_C32(0xC1059ED8), SPH_C32(0x367CD507), - SPH_C32(0x3070DD17), SPH_C32(0xF70E5939), - SPH_C32(0xFFC00B31), SPH_C32(0x68581511), - SPH_C32(0x64F98FA7), SPH_C32(0xBEFA4FA4) -}; - -static const sph_u32 IV256[8] = { - SPH_C32(0x6A09E667), SPH_C32(0xBB67AE85), - SPH_C32(0x3C6EF372), SPH_C32(0xA54FF53A), - SPH_C32(0x510E527F), SPH_C32(0x9B05688C), - SPH_C32(0x1F83D9AB), SPH_C32(0x5BE0CD19) -}; - -#if SPH_64 - -static const sph_u64 IV384[8] = { - SPH_C64(0xCBBB9D5DC1059ED8), SPH_C64(0x629A292A367CD507), - SPH_C64(0x9159015A3070DD17), SPH_C64(0x152FECD8F70E5939), - SPH_C64(0x67332667FFC00B31), SPH_C64(0x8EB44A8768581511), - SPH_C64(0xDB0C2E0D64F98FA7), SPH_C64(0x47B5481DBEFA4FA4) -}; - -static const sph_u64 IV512[8] = { - SPH_C64(0x6A09E667F3BCC908), SPH_C64(0xBB67AE8584CAA73B), - SPH_C64(0x3C6EF372FE94F82B), SPH_C64(0xA54FF53A5F1D36F1), - SPH_C64(0x510E527FADE682D1), SPH_C64(0x9B05688C2B3E6C1F), - SPH_C64(0x1F83D9ABFB41BD6B), SPH_C64(0x5BE0CD19137E2179) -}; - -#endif - -#if SPH_COMPACT_BLAKE_32 || SPH_COMPACT_BLAKE_64 - -static const unsigned sigma[16][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } -}; - -/* - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - 14 10 4 8 9 15 13 6 1 12 0 2 11 7 5 3 - 11 8 12 0 5 2 15 13 10 14 3 6 7 1 9 4 - 7 9 3 1 13 12 11 14 2 6 5 10 4 0 15 8 - 9 0 5 7 2 4 10 15 14 1 11 12 6 8 3 13 - 2 12 6 10 0 11 8 3 4 13 7 5 15 14 1 9 - 12 5 1 15 14 13 4 10 0 7 6 3 9 2 8 11 - 13 11 7 14 12 1 3 9 5 0 15 4 8 6 2 10 - 6 15 14 9 11 3 0 8 12 2 13 7 1 4 10 5 - 10 2 8 4 7 6 1 5 15 11 9 14 3 12 13 0 -*/ -#endif - -#define Z00 0 -#define Z01 1 -#define Z02 2 -#define Z03 3 -#define Z04 4 -#define Z05 5 -#define Z06 6 -#define Z07 7 -#define Z08 8 -#define Z09 9 -#define Z0A A -#define Z0B B -#define Z0C C -#define Z0D D -#define Z0E E -#define Z0F F - -#define Z10 E -#define Z11 A -#define Z12 4 -#define Z13 8 -#define Z14 9 -#define Z15 F -#define Z16 D -#define Z17 6 -#define Z18 1 -#define Z19 C -#define Z1A 0 -#define Z1B 2 -#define Z1C B -#define Z1D 7 -#define Z1E 5 -#define Z1F 3 - -#define Z20 B -#define Z21 8 -#define Z22 C -#define Z23 0 -#define Z24 5 -#define Z25 2 -#define Z26 F -#define Z27 D -#define Z28 A -#define Z29 E -#define Z2A 3 -#define Z2B 6 -#define Z2C 7 -#define Z2D 1 -#define Z2E 9 -#define Z2F 4 - -#define Z30 7 -#define Z31 9 -#define Z32 3 -#define Z33 1 -#define Z34 D -#define Z35 C -#define Z36 B -#define Z37 E -#define Z38 2 -#define Z39 6 -#define Z3A 5 -#define Z3B A -#define Z3C 4 -#define Z3D 0 -#define Z3E F -#define Z3F 8 - -#define Z40 9 -#define Z41 0 -#define Z42 5 -#define Z43 7 -#define Z44 2 -#define Z45 4 -#define Z46 A -#define Z47 F -#define Z48 E -#define Z49 1 -#define Z4A B -#define Z4B C -#define Z4C 6 -#define Z4D 8 -#define Z4E 3 -#define Z4F D - -#define Z50 2 -#define Z51 C -#define Z52 6 -#define Z53 A -#define Z54 0 -#define Z55 B -#define Z56 8 -#define Z57 3 -#define Z58 4 -#define Z59 D -#define Z5A 7 -#define Z5B 5 -#define Z5C F -#define Z5D E -#define Z5E 1 -#define Z5F 9 - -#define Z60 C -#define Z61 5 -#define Z62 1 -#define Z63 F -#define Z64 E -#define Z65 D -#define Z66 4 -#define Z67 A -#define Z68 0 -#define Z69 7 -#define Z6A 6 -#define Z6B 3 -#define Z6C 9 -#define Z6D 2 -#define Z6E 8 -#define Z6F B - -#define Z70 D -#define Z71 B -#define Z72 7 -#define Z73 E -#define Z74 C -#define Z75 1 -#define Z76 3 -#define Z77 9 -#define Z78 5 -#define Z79 0 -#define Z7A F -#define Z7B 4 -#define Z7C 8 -#define Z7D 6 -#define Z7E 2 -#define Z7F A - -#define Z80 6 -#define Z81 F -#define Z82 E -#define Z83 9 -#define Z84 B -#define Z85 3 -#define Z86 0 -#define Z87 8 -#define Z88 C -#define Z89 2 -#define Z8A D -#define Z8B 7 -#define Z8C 1 -#define Z8D 4 -#define Z8E A -#define Z8F 5 - -#define Z90 A -#define Z91 2 -#define Z92 8 -#define Z93 4 -#define Z94 7 -#define Z95 6 -#define Z96 1 -#define Z97 5 -#define Z98 F -#define Z99 B -#define Z9A 9 -#define Z9B E -#define Z9C 3 -#define Z9D C -#define Z9E D -#define Z9F 0 - -#define Mx(r, i) Mx_(Z ## r ## i) -#define Mx_(n) Mx__(n) -#define Mx__(n) M ## n - -#define CSx(r, i) CSx_(Z ## r ## i) -#define CSx_(n) CSx__(n) -#define CSx__(n) CS ## n - -#define CS0 SPH_C32(0x243F6A88) -#define CS1 SPH_C32(0x85A308D3) -#define CS2 SPH_C32(0x13198A2E) -#define CS3 SPH_C32(0x03707344) -#define CS4 SPH_C32(0xA4093822) -#define CS5 SPH_C32(0x299F31D0) -#define CS6 SPH_C32(0x082EFA98) -#define CS7 SPH_C32(0xEC4E6C89) -#define CS8 SPH_C32(0x452821E6) -#define CS9 SPH_C32(0x38D01377) -#define CSA SPH_C32(0xBE5466CF) -#define CSB SPH_C32(0x34E90C6C) -#define CSC SPH_C32(0xC0AC29B7) -#define CSD SPH_C32(0xC97C50DD) -#define CSE SPH_C32(0x3F84D5B5) -#define CSF SPH_C32(0xB5470917) - -#if SPH_COMPACT_BLAKE_32 - -static const sph_u32 CS[16] = { - SPH_C32(0x243F6A88), SPH_C32(0x85A308D3), - SPH_C32(0x13198A2E), SPH_C32(0x03707344), - SPH_C32(0xA4093822), SPH_C32(0x299F31D0), - SPH_C32(0x082EFA98), SPH_C32(0xEC4E6C89), - SPH_C32(0x452821E6), SPH_C32(0x38D01377), - SPH_C32(0xBE5466CF), SPH_C32(0x34E90C6C), - SPH_C32(0xC0AC29B7), SPH_C32(0xC97C50DD), - SPH_C32(0x3F84D5B5), SPH_C32(0xB5470917) -}; - -#endif - -#if SPH_64 - -#define CBx(r, i) CBx_(Z ## r ## i) -#define CBx_(n) CBx__(n) -#define CBx__(n) CB ## n - -#define CB0 SPH_C64(0x243F6A8885A308D3) -#define CB1 SPH_C64(0x13198A2E03707344) -#define CB2 SPH_C64(0xA4093822299F31D0) -#define CB3 SPH_C64(0x082EFA98EC4E6C89) -#define CB4 SPH_C64(0x452821E638D01377) -#define CB5 SPH_C64(0xBE5466CF34E90C6C) -#define CB6 SPH_C64(0xC0AC29B7C97C50DD) -#define CB7 SPH_C64(0x3F84D5B5B5470917) -#define CB8 SPH_C64(0x9216D5D98979FB1B) -#define CB9 SPH_C64(0xD1310BA698DFB5AC) -#define CBA SPH_C64(0x2FFD72DBD01ADFB7) -#define CBB SPH_C64(0xB8E1AFED6A267E96) -#define CBC SPH_C64(0xBA7C9045F12C7F99) -#define CBD SPH_C64(0x24A19947B3916CF7) -#define CBE SPH_C64(0x0801F2E2858EFC16) -#define CBF SPH_C64(0x636920D871574E69) - -#if SPH_COMPACT_BLAKE_64 - -static const sph_u64 CB[16] = { - SPH_C64(0x243F6A8885A308D3), SPH_C64(0x13198A2E03707344), - SPH_C64(0xA4093822299F31D0), SPH_C64(0x082EFA98EC4E6C89), - SPH_C64(0x452821E638D01377), SPH_C64(0xBE5466CF34E90C6C), - SPH_C64(0xC0AC29B7C97C50DD), SPH_C64(0x3F84D5B5B5470917), - SPH_C64(0x9216D5D98979FB1B), SPH_C64(0xD1310BA698DFB5AC), - SPH_C64(0x2FFD72DBD01ADFB7), SPH_C64(0xB8E1AFED6A267E96), - SPH_C64(0xBA7C9045F12C7F99), SPH_C64(0x24A19947B3916CF7), - SPH_C64(0x0801F2E2858EFC16), SPH_C64(0x636920D871574E69) -}; - -#endif - -#endif - -#define GS(m0, m1, c0, c1, a, b, c, d) do { \ - a = SPH_T32(a + b + (m0 ^ c1)); \ - d = SPH_ROTR32(d ^ a, 16); \ - c = SPH_T32(c + d); \ - b = SPH_ROTR32(b ^ c, 12); \ - a = SPH_T32(a + b + (m1 ^ c0)); \ - d = SPH_ROTR32(d ^ a, 8); \ - c = SPH_T32(c + d); \ - b = SPH_ROTR32(b ^ c, 7); \ - } while (0) - -#if SPH_COMPACT_BLAKE_32 - -#define ROUND_S(r) do { \ - GS(M[sigma[r][0x0]], M[sigma[r][0x1]], \ - CS[sigma[r][0x0]], CS[sigma[r][0x1]], V0, V4, V8, VC); \ - GS(M[sigma[r][0x2]], M[sigma[r][0x3]], \ - CS[sigma[r][0x2]], CS[sigma[r][0x3]], V1, V5, V9, VD); \ - GS(M[sigma[r][0x4]], M[sigma[r][0x5]], \ - CS[sigma[r][0x4]], CS[sigma[r][0x5]], V2, V6, VA, VE); \ - GS(M[sigma[r][0x6]], M[sigma[r][0x7]], \ - CS[sigma[r][0x6]], CS[sigma[r][0x7]], V3, V7, VB, VF); \ - GS(M[sigma[r][0x8]], M[sigma[r][0x9]], \ - CS[sigma[r][0x8]], CS[sigma[r][0x9]], V0, V5, VA, VF); \ - GS(M[sigma[r][0xA]], M[sigma[r][0xB]], \ - CS[sigma[r][0xA]], CS[sigma[r][0xB]], V1, V6, VB, VC); \ - GS(M[sigma[r][0xC]], M[sigma[r][0xD]], \ - CS[sigma[r][0xC]], CS[sigma[r][0xD]], V2, V7, V8, VD); \ - GS(M[sigma[r][0xE]], M[sigma[r][0xF]], \ - CS[sigma[r][0xE]], CS[sigma[r][0xF]], V3, V4, V9, VE); \ - } while (0) - -#else - -#define ROUND_S(r) do { \ - GS(Mx(r, 0), Mx(r, 1), CSx(r, 0), CSx(r, 1), V0, V4, V8, VC); \ - GS(Mx(r, 2), Mx(r, 3), CSx(r, 2), CSx(r, 3), V1, V5, V9, VD); \ - GS(Mx(r, 4), Mx(r, 5), CSx(r, 4), CSx(r, 5), V2, V6, VA, VE); \ - GS(Mx(r, 6), Mx(r, 7), CSx(r, 6), CSx(r, 7), V3, V7, VB, VF); \ - GS(Mx(r, 8), Mx(r, 9), CSx(r, 8), CSx(r, 9), V0, V5, VA, VF); \ - GS(Mx(r, A), Mx(r, B), CSx(r, A), CSx(r, B), V1, V6, VB, VC); \ - GS(Mx(r, C), Mx(r, D), CSx(r, C), CSx(r, D), V2, V7, V8, VD); \ - GS(Mx(r, E), Mx(r, F), CSx(r, E), CSx(r, F), V3, V4, V9, VE); \ - } while (0) - -#endif - -#if SPH_64 - -#define GB(m0, m1, c0, c1, a, b, c, d) do { \ - a = SPH_T64(a + b + (m0 ^ c1)); \ - d = SPH_ROTR64(d ^ a, 32); \ - c = SPH_T64(c + d); \ - b = SPH_ROTR64(b ^ c, 25); \ - a = SPH_T64(a + b + (m1 ^ c0)); \ - d = SPH_ROTR64(d ^ a, 16); \ - c = SPH_T64(c + d); \ - b = SPH_ROTR64(b ^ c, 11); \ - } while (0) - -#if SPH_COMPACT_BLAKE_64 - -#define ROUND_B(r) do { \ - GB(M[sigma[r][0x0]], M[sigma[r][0x1]], \ - CB[sigma[r][0x0]], CB[sigma[r][0x1]], V0, V4, V8, VC); \ - GB(M[sigma[r][0x2]], M[sigma[r][0x3]], \ - CB[sigma[r][0x2]], CB[sigma[r][0x3]], V1, V5, V9, VD); \ - GB(M[sigma[r][0x4]], M[sigma[r][0x5]], \ - CB[sigma[r][0x4]], CB[sigma[r][0x5]], V2, V6, VA, VE); \ - GB(M[sigma[r][0x6]], M[sigma[r][0x7]], \ - CB[sigma[r][0x6]], CB[sigma[r][0x7]], V3, V7, VB, VF); \ - GB(M[sigma[r][0x8]], M[sigma[r][0x9]], \ - CB[sigma[r][0x8]], CB[sigma[r][0x9]], V0, V5, VA, VF); \ - GB(M[sigma[r][0xA]], M[sigma[r][0xB]], \ - CB[sigma[r][0xA]], CB[sigma[r][0xB]], V1, V6, VB, VC); \ - GB(M[sigma[r][0xC]], M[sigma[r][0xD]], \ - CB[sigma[r][0xC]], CB[sigma[r][0xD]], V2, V7, V8, VD); \ - GB(M[sigma[r][0xE]], M[sigma[r][0xF]], \ - CB[sigma[r][0xE]], CB[sigma[r][0xF]], V3, V4, V9, VE); \ - } while (0) - -#else - -#define ROUND_B(r) do { \ - GB(Mx(r, 0), Mx(r, 1), CBx(r, 0), CBx(r, 1), V0, V4, V8, VC); \ - GB(Mx(r, 2), Mx(r, 3), CBx(r, 2), CBx(r, 3), V1, V5, V9, VD); \ - GB(Mx(r, 4), Mx(r, 5), CBx(r, 4), CBx(r, 5), V2, V6, VA, VE); \ - GB(Mx(r, 6), Mx(r, 7), CBx(r, 6), CBx(r, 7), V3, V7, VB, VF); \ - GB(Mx(r, 8), Mx(r, 9), CBx(r, 8), CBx(r, 9), V0, V5, VA, VF); \ - GB(Mx(r, A), Mx(r, B), CBx(r, A), CBx(r, B), V1, V6, VB, VC); \ - GB(Mx(r, C), Mx(r, D), CBx(r, C), CBx(r, D), V2, V7, V8, VD); \ - GB(Mx(r, E), Mx(r, F), CBx(r, E), CBx(r, F), V3, V4, V9, VE); \ - } while (0) - -#endif - -#endif - -#define DECL_STATE32 \ - sph_u32 H0, H1, H2, H3, H4, H5, H6, H7; \ - sph_u32 S0, S1, S2, S3, T0, T1; - -#define READ_STATE32(state) do { \ - H0 = (state)->H[0]; \ - H1 = (state)->H[1]; \ - H2 = (state)->H[2]; \ - H3 = (state)->H[3]; \ - H4 = (state)->H[4]; \ - H5 = (state)->H[5]; \ - H6 = (state)->H[6]; \ - H7 = (state)->H[7]; \ - S0 = (state)->S[0]; \ - S1 = (state)->S[1]; \ - S2 = (state)->S[2]; \ - S3 = (state)->S[3]; \ - T0 = (state)->T0; \ - T1 = (state)->T1; \ - } while (0) - -#define WRITE_STATE32(state) do { \ - (state)->H[0] = H0; \ - (state)->H[1] = H1; \ - (state)->H[2] = H2; \ - (state)->H[3] = H3; \ - (state)->H[4] = H4; \ - (state)->H[5] = H5; \ - (state)->H[6] = H6; \ - (state)->H[7] = H7; \ - (state)->S[0] = S0; \ - (state)->S[1] = S1; \ - (state)->S[2] = S2; \ - (state)->S[3] = S3; \ - (state)->T0 = T0; \ - (state)->T1 = T1; \ - } while (0) - -#if SPH_COMPACT_BLAKE_32 - -#define COMPRESS32 do { \ - sph_u32 M[16]; \ - sph_u32 V0, V1, V2, V3, V4, V5, V6, V7; \ - sph_u32 V8, V9, VA, VB, VC, VD, VE, VF; \ - unsigned r; \ - V0 = H0; \ - V1 = H1; \ - V2 = H2; \ - V3 = H3; \ - V4 = H4; \ - V5 = H5; \ - V6 = H6; \ - V7 = H7; \ - V8 = S0 ^ CS0; \ - V9 = S1 ^ CS1; \ - VA = S2 ^ CS2; \ - VB = S3 ^ CS3; \ - VC = T0 ^ CS4; \ - VD = T0 ^ CS5; \ - VE = T1 ^ CS6; \ - VF = T1 ^ CS7; \ - M[0x0] = sph_dec32be_aligned(buf + 0); \ - M[0x1] = sph_dec32be_aligned(buf + 4); \ - M[0x2] = sph_dec32be_aligned(buf + 8); \ - M[0x3] = sph_dec32be_aligned(buf + 12); \ - M[0x4] = sph_dec32be_aligned(buf + 16); \ - M[0x5] = sph_dec32be_aligned(buf + 20); \ - M[0x6] = sph_dec32be_aligned(buf + 24); \ - M[0x7] = sph_dec32be_aligned(buf + 28); \ - M[0x8] = sph_dec32be_aligned(buf + 32); \ - M[0x9] = sph_dec32be_aligned(buf + 36); \ - M[0xA] = sph_dec32be_aligned(buf + 40); \ - M[0xB] = sph_dec32be_aligned(buf + 44); \ - M[0xC] = sph_dec32be_aligned(buf + 48); \ - M[0xD] = sph_dec32be_aligned(buf + 52); \ - M[0xE] = sph_dec32be_aligned(buf + 56); \ - M[0xF] = sph_dec32be_aligned(buf + 60); \ - for (r = 0; r < 14; r ++) \ - ROUND_S(r); \ - H0 ^= S0 ^ V0 ^ V8; \ - H1 ^= S1 ^ V1 ^ V9; \ - H2 ^= S2 ^ V2 ^ VA; \ - H3 ^= S3 ^ V3 ^ VB; \ - H4 ^= S0 ^ V4 ^ VC; \ - H5 ^= S1 ^ V5 ^ VD; \ - H6 ^= S2 ^ V6 ^ VE; \ - H7 ^= S3 ^ V7 ^ VF; \ - } while (0) - -#else - -#define COMPRESS32 do { \ - sph_u32 M0, M1, M2, M3, M4, M5, M6, M7; \ - sph_u32 M8, M9, MA, MB, MC, MD, ME, MF; \ - sph_u32 V0, V1, V2, V3, V4, V5, V6, V7; \ - sph_u32 V8, V9, VA, VB, VC, VD, VE, VF; \ - V0 = H0; \ - V1 = H1; \ - V2 = H2; \ - V3 = H3; \ - V4 = H4; \ - V5 = H5; \ - V6 = H6; \ - V7 = H7; \ - V8 = S0 ^ CS0; \ - V9 = S1 ^ CS1; \ - VA = S2 ^ CS2; \ - VB = S3 ^ CS3; \ - VC = T0 ^ CS4; \ - VD = T0 ^ CS5; \ - VE = T1 ^ CS6; \ - VF = T1 ^ CS7; \ - M0 = sph_dec32be_aligned(buf + 0); \ - M1 = sph_dec32be_aligned(buf + 4); \ - M2 = sph_dec32be_aligned(buf + 8); \ - M3 = sph_dec32be_aligned(buf + 12); \ - M4 = sph_dec32be_aligned(buf + 16); \ - M5 = sph_dec32be_aligned(buf + 20); \ - M6 = sph_dec32be_aligned(buf + 24); \ - M7 = sph_dec32be_aligned(buf + 28); \ - M8 = sph_dec32be_aligned(buf + 32); \ - M9 = sph_dec32be_aligned(buf + 36); \ - MA = sph_dec32be_aligned(buf + 40); \ - MB = sph_dec32be_aligned(buf + 44); \ - MC = sph_dec32be_aligned(buf + 48); \ - MD = sph_dec32be_aligned(buf + 52); \ - ME = sph_dec32be_aligned(buf + 56); \ - MF = sph_dec32be_aligned(buf + 60); \ - ROUND_S(0); \ - ROUND_S(1); \ - ROUND_S(2); \ - ROUND_S(3); \ - ROUND_S(4); \ - ROUND_S(5); \ - ROUND_S(6); \ - ROUND_S(7); \ - ROUND_S(8); \ - ROUND_S(9); \ - ROUND_S(0); \ - ROUND_S(1); \ - ROUND_S(2); \ - ROUND_S(3); \ - H0 ^= S0 ^ V0 ^ V8; \ - H1 ^= S1 ^ V1 ^ V9; \ - H2 ^= S2 ^ V2 ^ VA; \ - H3 ^= S3 ^ V3 ^ VB; \ - H4 ^= S0 ^ V4 ^ VC; \ - H5 ^= S1 ^ V5 ^ VD; \ - H6 ^= S2 ^ V6 ^ VE; \ - H7 ^= S3 ^ V7 ^ VF; \ - } while (0) - -#endif - -#if SPH_64 - -#define DECL_STATE64 \ - sph_u64 H0, H1, H2, H3, H4, H5, H6, H7; \ - sph_u64 S0, S1, S2, S3, T0, T1; - -#define READ_STATE64(state) do { \ - H0 = (state)->H[0]; \ - H1 = (state)->H[1]; \ - H2 = (state)->H[2]; \ - H3 = (state)->H[3]; \ - H4 = (state)->H[4]; \ - H5 = (state)->H[5]; \ - H6 = (state)->H[6]; \ - H7 = (state)->H[7]; \ - S0 = (state)->S[0]; \ - S1 = (state)->S[1]; \ - S2 = (state)->S[2]; \ - S3 = (state)->S[3]; \ - T0 = (state)->T0; \ - T1 = (state)->T1; \ - } while (0) - -#define WRITE_STATE64(state) do { \ - (state)->H[0] = H0; \ - (state)->H[1] = H1; \ - (state)->H[2] = H2; \ - (state)->H[3] = H3; \ - (state)->H[4] = H4; \ - (state)->H[5] = H5; \ - (state)->H[6] = H6; \ - (state)->H[7] = H7; \ - (state)->S[0] = S0; \ - (state)->S[1] = S1; \ - (state)->S[2] = S2; \ - (state)->S[3] = S3; \ - (state)->T0 = T0; \ - (state)->T1 = T1; \ - } while (0) - -#if SPH_COMPACT_BLAKE_64 - -#define COMPRESS64 do { \ - sph_u64 M[16]; \ - sph_u64 V0, V1, V2, V3, V4, V5, V6, V7; \ - sph_u64 V8, V9, VA, VB, VC, VD, VE, VF; \ - unsigned r; \ - V0 = H0; \ - V1 = H1; \ - V2 = H2; \ - V3 = H3; \ - V4 = H4; \ - V5 = H5; \ - V6 = H6; \ - V7 = H7; \ - V8 = S0 ^ CB0; \ - V9 = S1 ^ CB1; \ - VA = S2 ^ CB2; \ - VB = S3 ^ CB3; \ - VC = T0 ^ CB4; \ - VD = T0 ^ CB5; \ - VE = T1 ^ CB6; \ - VF = T1 ^ CB7; \ - M[0x0] = sph_dec64be_aligned(buf + 0); \ - M[0x1] = sph_dec64be_aligned(buf + 8); \ - M[0x2] = sph_dec64be_aligned(buf + 16); \ - M[0x3] = sph_dec64be_aligned(buf + 24); \ - M[0x4] = sph_dec64be_aligned(buf + 32); \ - M[0x5] = sph_dec64be_aligned(buf + 40); \ - M[0x6] = sph_dec64be_aligned(buf + 48); \ - M[0x7] = sph_dec64be_aligned(buf + 56); \ - M[0x8] = sph_dec64be_aligned(buf + 64); \ - M[0x9] = sph_dec64be_aligned(buf + 72); \ - M[0xA] = sph_dec64be_aligned(buf + 80); \ - M[0xB] = sph_dec64be_aligned(buf + 88); \ - M[0xC] = sph_dec64be_aligned(buf + 96); \ - M[0xD] = sph_dec64be_aligned(buf + 104); \ - M[0xE] = sph_dec64be_aligned(buf + 112); \ - M[0xF] = sph_dec64be_aligned(buf + 120); \ - for (r = 0; r < 16; r ++) \ - ROUND_B(r); \ - H0 ^= S0 ^ V0 ^ V8; \ - H1 ^= S1 ^ V1 ^ V9; \ - H2 ^= S2 ^ V2 ^ VA; \ - H3 ^= S3 ^ V3 ^ VB; \ - H4 ^= S0 ^ V4 ^ VC; \ - H5 ^= S1 ^ V5 ^ VD; \ - H6 ^= S2 ^ V6 ^ VE; \ - H7 ^= S3 ^ V7 ^ VF; \ - } while (0) - -#else - -#define COMPRESS64 do { \ - sph_u64 M0, M1, M2, M3, M4, M5, M6, M7; \ - sph_u64 M8, M9, MA, MB, MC, MD, ME, MF; \ - sph_u64 V0, V1, V2, V3, V4, V5, V6, V7; \ - sph_u64 V8, V9, VA, VB, VC, VD, VE, VF; \ - V0 = H0; \ - V1 = H1; \ - V2 = H2; \ - V3 = H3; \ - V4 = H4; \ - V5 = H5; \ - V6 = H6; \ - V7 = H7; \ - V8 = S0 ^ CB0; \ - V9 = S1 ^ CB1; \ - VA = S2 ^ CB2; \ - VB = S3 ^ CB3; \ - VC = T0 ^ CB4; \ - VD = T0 ^ CB5; \ - VE = T1 ^ CB6; \ - VF = T1 ^ CB7; \ - M0 = sph_dec64be_aligned(buf + 0); \ - M1 = sph_dec64be_aligned(buf + 8); \ - M2 = sph_dec64be_aligned(buf + 16); \ - M3 = sph_dec64be_aligned(buf + 24); \ - M4 = sph_dec64be_aligned(buf + 32); \ - M5 = sph_dec64be_aligned(buf + 40); \ - M6 = sph_dec64be_aligned(buf + 48); \ - M7 = sph_dec64be_aligned(buf + 56); \ - M8 = sph_dec64be_aligned(buf + 64); \ - M9 = sph_dec64be_aligned(buf + 72); \ - MA = sph_dec64be_aligned(buf + 80); \ - MB = sph_dec64be_aligned(buf + 88); \ - MC = sph_dec64be_aligned(buf + 96); \ - MD = sph_dec64be_aligned(buf + 104); \ - ME = sph_dec64be_aligned(buf + 112); \ - MF = sph_dec64be_aligned(buf + 120); \ - ROUND_B(0); \ - ROUND_B(1); \ - ROUND_B(2); \ - ROUND_B(3); \ - ROUND_B(4); \ - ROUND_B(5); \ - ROUND_B(6); \ - ROUND_B(7); \ - ROUND_B(8); \ - ROUND_B(9); \ - ROUND_B(0); \ - ROUND_B(1); \ - ROUND_B(2); \ - ROUND_B(3); \ - ROUND_B(4); \ - ROUND_B(5); \ - H0 ^= S0 ^ V0 ^ V8; \ - H1 ^= S1 ^ V1 ^ V9; \ - H2 ^= S2 ^ V2 ^ VA; \ - H3 ^= S3 ^ V3 ^ VB; \ - H4 ^= S0 ^ V4 ^ VC; \ - H5 ^= S1 ^ V5 ^ VD; \ - H6 ^= S2 ^ V6 ^ VE; \ - H7 ^= S3 ^ V7 ^ VF; \ - } while (0) - -#endif - -#endif - -static const sph_u32 salt_zero_small[4] = { 0, 0, 0, 0 }; - -static void -blake32_init(sph_blake_small_context *sc, - const sph_u32 *iv, const sph_u32 *salt) -{ - memcpy(sc->H, iv, 8 * sizeof(sph_u32)); - memcpy(sc->S, salt, 4 * sizeof(sph_u32)); - sc->T0 = sc->T1 = 0; - sc->ptr = 0; -} - -static void -blake32(sph_blake_small_context *sc, const void *data, size_t len) -{ - unsigned char *buf; - size_t ptr; - DECL_STATE32 - - buf = sc->buf; - ptr = sc->ptr; - if (len < (sizeof sc->buf) - ptr) { - memcpy(buf + ptr, data, len); - ptr += len; - sc->ptr = ptr; - return; - } - - READ_STATE32(sc); - while (len > 0) { - size_t clen; - - clen = (sizeof sc->buf) - ptr; - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - ptr += clen; - data = (const unsigned char *)data + clen; - len -= clen; - if (ptr == sizeof sc->buf) { - if ((T0 = SPH_T32(T0 + 512)) < 512) - T1 = SPH_T32(T1 + 1); - COMPRESS32; - ptr = 0; - } - } - WRITE_STATE32(sc); - sc->ptr = ptr; -} - -static void -blake32_close(sph_blake_small_context *sc, - unsigned ub, unsigned n, void *dst, size_t out_size_w32) -{ - union { - unsigned char buf[64]; - sph_u32 dummy; - } u; - size_t ptr, k; - unsigned bit_len; - unsigned z; - sph_u32 th, tl; - unsigned char *out; - - ptr = sc->ptr; - bit_len = ((unsigned)ptr << 3) + n; - z = 0x80 >> n; - u.buf[ptr] = ((ub & -z) | z) & 0xFF; - tl = sc->T0 + bit_len; - th = sc->T1; - if (ptr == 0 && n == 0) { - sc->T0 = SPH_C32(0xFFFFFE00); - sc->T1 = SPH_C32(0xFFFFFFFF); - } else if (sc->T0 == 0) { - sc->T0 = SPH_C32(0xFFFFFE00) + bit_len; - sc->T1 = SPH_T32(sc->T1 - 1); - } else { - sc->T0 -= 512 - bit_len; - } - if (bit_len <= 446) { - memset(u.buf + ptr + 1, 0, 55 - ptr); - if (out_size_w32 == 8) - u.buf[55] |= 1; - sph_enc32be_aligned(u.buf + 56, th); - sph_enc32be_aligned(u.buf + 60, tl); - blake32(sc, u.buf + ptr, 64 - ptr); - } else { - memset(u.buf + ptr + 1, 0, 63 - ptr); - blake32(sc, u.buf + ptr, 64 - ptr); - sc->T0 = SPH_C32(0xFFFFFE00); - sc->T1 = SPH_C32(0xFFFFFFFF); - memset(u.buf, 0, 56); - if (out_size_w32 == 8) - u.buf[55] = 1; - sph_enc32be_aligned(u.buf + 56, th); - sph_enc32be_aligned(u.buf + 60, tl); - blake32(sc, u.buf, 64); - } - out = dst; - for (k = 0; k < out_size_w32; k ++) - sph_enc32be(out + (k << 2), sc->H[k]); -} - -#if SPH_64 - -static const sph_u64 salt_zero_big[4] = { 0, 0, 0, 0 }; - -static void -blake64_init(sph_blake_big_context *sc, - const sph_u64 *iv, const sph_u64 *salt) -{ - memcpy(sc->H, iv, 8 * sizeof(sph_u64)); - memcpy(sc->S, salt, 4 * sizeof(sph_u64)); - sc->T0 = sc->T1 = 0; - sc->ptr = 0; -} - -static void -blake64(sph_blake_big_context *sc, const void *data, size_t len) -{ - unsigned char *buf; - size_t ptr; - DECL_STATE64 - - buf = sc->buf; - ptr = sc->ptr; - if (len < (sizeof sc->buf) - ptr) { - memcpy(buf + ptr, data, len); - ptr += len; - sc->ptr = ptr; - return; - } - - READ_STATE64(sc); - while (len > 0) { - size_t clen; - - clen = (sizeof sc->buf) - ptr; - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - ptr += clen; - data = (const unsigned char *)data + clen; - len -= clen; - if (ptr == sizeof sc->buf) { - if ((T0 = SPH_T64(T0 + 1024)) < 1024) - T1 = SPH_T64(T1 + 1); - COMPRESS64; - ptr = 0; - } - } - WRITE_STATE64(sc); - sc->ptr = ptr; -} - -static void -blake64_close(sph_blake_big_context *sc, - unsigned ub, unsigned n, void *dst, size_t out_size_w64) -{ - union { - unsigned char buf[128]; - sph_u64 dummy; - } u; - size_t ptr, k; - unsigned bit_len; - unsigned z; - sph_u64 th, tl; - unsigned char *out; - - ptr = sc->ptr; - bit_len = ((unsigned)ptr << 3) + n; - z = 0x80 >> n; - u.buf[ptr] = ((ub & -z) | z) & 0xFF; - tl = sc->T0 + bit_len; - th = sc->T1; - if (ptr == 0 && n == 0) { - sc->T0 = SPH_C64(0xFFFFFFFFFFFFFC00); - sc->T1 = SPH_C64(0xFFFFFFFFFFFFFFFF); - } else if (sc->T0 == 0) { - sc->T0 = SPH_C64(0xFFFFFFFFFFFFFC00) + bit_len; - sc->T1 = SPH_T64(sc->T1 - 1); - } else { - sc->T0 -= 1024 - bit_len; - } - if (bit_len <= 894) { - memset(u.buf + ptr + 1, 0, 111 - ptr); - if (out_size_w64 == 8) - u.buf[111] |= 1; - sph_enc64be_aligned(u.buf + 112, th); - sph_enc64be_aligned(u.buf + 120, tl); - blake64(sc, u.buf + ptr, 128 - ptr); - } else { - memset(u.buf + ptr + 1, 0, 127 - ptr); - blake64(sc, u.buf + ptr, 128 - ptr); - sc->T0 = SPH_C64(0xFFFFFFFFFFFFFC00); - sc->T1 = SPH_C64(0xFFFFFFFFFFFFFFFF); - memset(u.buf, 0, 112); - if (out_size_w64 == 8) - u.buf[111] = 1; - sph_enc64be_aligned(u.buf + 112, th); - sph_enc64be_aligned(u.buf + 120, tl); - blake64(sc, u.buf, 128); - } - out = dst; - for (k = 0; k < out_size_w64; k ++) - sph_enc64be(out + (k << 3), sc->H[k]); -} - -#endif - -/* see sph_blake.h */ -void -sph_blake224_init(void *cc) -{ - blake32_init(cc, IV224, salt_zero_small); -} - -/* see sph_blake.h */ -void -sph_blake224(void *cc, const void *data, size_t len) -{ - blake32(cc, data, len); -} - -/* see sph_blake.h */ -void -sph_blake224_close(void *cc, void *dst) -{ - sph_blake224_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_blake.h */ -void -sph_blake224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - blake32_close(cc, ub, n, dst, 7); - sph_blake224_init(cc); -} - -/* see sph_blake.h */ -void -sph_blake256_init(void *cc) -{ - blake32_init(cc, IV256, salt_zero_small); -} - -/* see sph_blake.h */ -void -sph_blake256(void *cc, const void *data, size_t len) -{ - blake32(cc, data, len); -} - -/* see sph_blake.h */ -void -sph_blake256_close(void *cc, void *dst) -{ - sph_blake256_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_blake.h */ -void -sph_blake256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - blake32_close(cc, ub, n, dst, 8); - sph_blake256_init(cc); -} - -#if SPH_64 - -/* see sph_blake.h */ -void -sph_blake384_init(void *cc) -{ - blake64_init(cc, IV384, salt_zero_big); -} - -/* see sph_blake.h */ -void -sph_blake384(void *cc, const void *data, size_t len) -{ - blake64(cc, data, len); -} - -/* see sph_blake.h */ -void -sph_blake384_close(void *cc, void *dst) -{ - sph_blake384_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_blake.h */ -void -sph_blake384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - blake64_close(cc, ub, n, dst, 6); - sph_blake384_init(cc); -} - -/* see sph_blake.h */ -void -sph_blake512_init(void *cc) -{ - blake64_init(cc, IV512, salt_zero_big); -} - -/* see sph_blake.h */ -void -sph_blake512(void *cc, const void *data, size_t len) -{ - blake64(cc, data, len); -} - -/* see sph_blake.h */ -void -sph_blake512_close(void *cc, void *dst) -{ - sph_blake512_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_blake.h */ -void -sph_blake512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - blake64_close(cc, ub, n, dst, 8); - sph_blake512_init(cc); -} - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/src/crypto/bmw.c b/src/crypto/bmw.c deleted file mode 100644 index b89a881e80c8..000000000000 --- a/src/crypto/bmw.c +++ /dev/null @@ -1,965 +0,0 @@ -/* $Id: bmw.c 227 2010-06-16 17:28:38Z tp $ */ -/* - * BMW implementation. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @author Thomas Pornin - */ - -#include -#include -#include - -#ifdef __cplusplus -extern "C"{ -#endif - -#include "sph_bmw.h" - -#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_BMW -#define SPH_SMALL_FOOTPRINT_BMW 1 -#endif - -#ifdef _MSC_VER -#pragma warning (disable: 4146) -#endif - -static const sph_u32 IV224[] = { - SPH_C32(0x00010203), SPH_C32(0x04050607), - SPH_C32(0x08090A0B), SPH_C32(0x0C0D0E0F), - SPH_C32(0x10111213), SPH_C32(0x14151617), - SPH_C32(0x18191A1B), SPH_C32(0x1C1D1E1F), - SPH_C32(0x20212223), SPH_C32(0x24252627), - SPH_C32(0x28292A2B), SPH_C32(0x2C2D2E2F), - SPH_C32(0x30313233), SPH_C32(0x34353637), - SPH_C32(0x38393A3B), SPH_C32(0x3C3D3E3F) -}; - -static const sph_u32 IV256[] = { - SPH_C32(0x40414243), SPH_C32(0x44454647), - SPH_C32(0x48494A4B), SPH_C32(0x4C4D4E4F), - SPH_C32(0x50515253), SPH_C32(0x54555657), - SPH_C32(0x58595A5B), SPH_C32(0x5C5D5E5F), - SPH_C32(0x60616263), SPH_C32(0x64656667), - SPH_C32(0x68696A6B), SPH_C32(0x6C6D6E6F), - SPH_C32(0x70717273), SPH_C32(0x74757677), - SPH_C32(0x78797A7B), SPH_C32(0x7C7D7E7F) -}; - -#if SPH_64 - -static const sph_u64 IV384[] = { - SPH_C64(0x0001020304050607), SPH_C64(0x08090A0B0C0D0E0F), - SPH_C64(0x1011121314151617), SPH_C64(0x18191A1B1C1D1E1F), - SPH_C64(0x2021222324252627), SPH_C64(0x28292A2B2C2D2E2F), - SPH_C64(0x3031323334353637), SPH_C64(0x38393A3B3C3D3E3F), - SPH_C64(0x4041424344454647), SPH_C64(0x48494A4B4C4D4E4F), - SPH_C64(0x5051525354555657), SPH_C64(0x58595A5B5C5D5E5F), - SPH_C64(0x6061626364656667), SPH_C64(0x68696A6B6C6D6E6F), - SPH_C64(0x7071727374757677), SPH_C64(0x78797A7B7C7D7E7F) -}; - -static const sph_u64 IV512[] = { - SPH_C64(0x8081828384858687), SPH_C64(0x88898A8B8C8D8E8F), - SPH_C64(0x9091929394959697), SPH_C64(0x98999A9B9C9D9E9F), - SPH_C64(0xA0A1A2A3A4A5A6A7), SPH_C64(0xA8A9AAABACADAEAF), - SPH_C64(0xB0B1B2B3B4B5B6B7), SPH_C64(0xB8B9BABBBCBDBEBF), - SPH_C64(0xC0C1C2C3C4C5C6C7), SPH_C64(0xC8C9CACBCCCDCECF), - SPH_C64(0xD0D1D2D3D4D5D6D7), SPH_C64(0xD8D9DADBDCDDDEDF), - SPH_C64(0xE0E1E2E3E4E5E6E7), SPH_C64(0xE8E9EAEBECEDEEEF), - SPH_C64(0xF0F1F2F3F4F5F6F7), SPH_C64(0xF8F9FAFBFCFDFEFF) -}; - -#endif - -#define XCAT(x, y) XCAT_(x, y) -#define XCAT_(x, y) x ## y - -#define LPAR ( - -#define I16_16 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 -#define I16_17 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 -#define I16_18 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 -#define I16_19 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 -#define I16_20 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 -#define I16_21 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 -#define I16_22 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 -#define I16_23 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 -#define I16_24 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 -#define I16_25 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 -#define I16_26 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 -#define I16_27 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 -#define I16_28 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 -#define I16_29 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 -#define I16_30 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29 -#define I16_31 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 - -#define M16_16 0, 1, 3, 4, 7, 10, 11 -#define M16_17 1, 2, 4, 5, 8, 11, 12 -#define M16_18 2, 3, 5, 6, 9, 12, 13 -#define M16_19 3, 4, 6, 7, 10, 13, 14 -#define M16_20 4, 5, 7, 8, 11, 14, 15 -#define M16_21 5, 6, 8, 9, 12, 15, 16 -#define M16_22 6, 7, 9, 10, 13, 0, 1 -#define M16_23 7, 8, 10, 11, 14, 1, 2 -#define M16_24 8, 9, 11, 12, 15, 2, 3 -#define M16_25 9, 10, 12, 13, 0, 3, 4 -#define M16_26 10, 11, 13, 14, 1, 4, 5 -#define M16_27 11, 12, 14, 15, 2, 5, 6 -#define M16_28 12, 13, 15, 16, 3, 6, 7 -#define M16_29 13, 14, 0, 1, 4, 7, 8 -#define M16_30 14, 15, 1, 2, 5, 8, 9 -#define M16_31 15, 16, 2, 3, 6, 9, 10 - -#define ss0(x) (((x) >> 1) ^ SPH_T32((x) << 3) \ - ^ SPH_ROTL32(x, 4) ^ SPH_ROTL32(x, 19)) -#define ss1(x) (((x) >> 1) ^ SPH_T32((x) << 2) \ - ^ SPH_ROTL32(x, 8) ^ SPH_ROTL32(x, 23)) -#define ss2(x) (((x) >> 2) ^ SPH_T32((x) << 1) \ - ^ SPH_ROTL32(x, 12) ^ SPH_ROTL32(x, 25)) -#define ss3(x) (((x) >> 2) ^ SPH_T32((x) << 2) \ - ^ SPH_ROTL32(x, 15) ^ SPH_ROTL32(x, 29)) -#define ss4(x) (((x) >> 1) ^ (x)) -#define ss5(x) (((x) >> 2) ^ (x)) -#define rs1(x) SPH_ROTL32(x, 3) -#define rs2(x) SPH_ROTL32(x, 7) -#define rs3(x) SPH_ROTL32(x, 13) -#define rs4(x) SPH_ROTL32(x, 16) -#define rs5(x) SPH_ROTL32(x, 19) -#define rs6(x) SPH_ROTL32(x, 23) -#define rs7(x) SPH_ROTL32(x, 27) - -#define Ks(j) SPH_T32((sph_u32)(j) * SPH_C32(0x05555555)) - -#define add_elt_s(mf, hf, j0m, j1m, j3m, j4m, j7m, j10m, j11m, j16) \ - (SPH_T32(SPH_ROTL32(mf(j0m), j1m) + SPH_ROTL32(mf(j3m), j4m) \ - - SPH_ROTL32(mf(j10m), j11m) + Ks(j16)) ^ hf(j7m)) - -#define expand1s_inner(qf, mf, hf, i16, \ - i0, i1, i2, i3, i4, i5, i6, i7, i8, \ - i9, i10, i11, i12, i13, i14, i15, \ - i0m, i1m, i3m, i4m, i7m, i10m, i11m) \ - SPH_T32(ss1(qf(i0)) + ss2(qf(i1)) + ss3(qf(i2)) + ss0(qf(i3)) \ - + ss1(qf(i4)) + ss2(qf(i5)) + ss3(qf(i6)) + ss0(qf(i7)) \ - + ss1(qf(i8)) + ss2(qf(i9)) + ss3(qf(i10)) + ss0(qf(i11)) \ - + ss1(qf(i12)) + ss2(qf(i13)) + ss3(qf(i14)) + ss0(qf(i15)) \ - + add_elt_s(mf, hf, i0m, i1m, i3m, i4m, i7m, i10m, i11m, i16)) - -#define expand1s(qf, mf, hf, i16) \ - expand1s_(qf, mf, hf, i16, I16_ ## i16, M16_ ## i16) -#define expand1s_(qf, mf, hf, i16, ix, iy) \ - expand1s_inner LPAR qf, mf, hf, i16, ix, iy) - -#define expand2s_inner(qf, mf, hf, i16, \ - i0, i1, i2, i3, i4, i5, i6, i7, i8, \ - i9, i10, i11, i12, i13, i14, i15, \ - i0m, i1m, i3m, i4m, i7m, i10m, i11m) \ - SPH_T32(qf(i0) + rs1(qf(i1)) + qf(i2) + rs2(qf(i3)) \ - + qf(i4) + rs3(qf(i5)) + qf(i6) + rs4(qf(i7)) \ - + qf(i8) + rs5(qf(i9)) + qf(i10) + rs6(qf(i11)) \ - + qf(i12) + rs7(qf(i13)) + ss4(qf(i14)) + ss5(qf(i15)) \ - + add_elt_s(mf, hf, i0m, i1m, i3m, i4m, i7m, i10m, i11m, i16)) - -#define expand2s(qf, mf, hf, i16) \ - expand2s_(qf, mf, hf, i16, I16_ ## i16, M16_ ## i16) -#define expand2s_(qf, mf, hf, i16, ix, iy) \ - expand2s_inner LPAR qf, mf, hf, i16, ix, iy) - -#if SPH_64 - -#define sb0(x) (((x) >> 1) ^ SPH_T64((x) << 3) \ - ^ SPH_ROTL64(x, 4) ^ SPH_ROTL64(x, 37)) -#define sb1(x) (((x) >> 1) ^ SPH_T64((x) << 2) \ - ^ SPH_ROTL64(x, 13) ^ SPH_ROTL64(x, 43)) -#define sb2(x) (((x) >> 2) ^ SPH_T64((x) << 1) \ - ^ SPH_ROTL64(x, 19) ^ SPH_ROTL64(x, 53)) -#define sb3(x) (((x) >> 2) ^ SPH_T64((x) << 2) \ - ^ SPH_ROTL64(x, 28) ^ SPH_ROTL64(x, 59)) -#define sb4(x) (((x) >> 1) ^ (x)) -#define sb5(x) (((x) >> 2) ^ (x)) -#define rb1(x) SPH_ROTL64(x, 5) -#define rb2(x) SPH_ROTL64(x, 11) -#define rb3(x) SPH_ROTL64(x, 27) -#define rb4(x) SPH_ROTL64(x, 32) -#define rb5(x) SPH_ROTL64(x, 37) -#define rb6(x) SPH_ROTL64(x, 43) -#define rb7(x) SPH_ROTL64(x, 53) - -#define Kb(j) SPH_T64((sph_u64)(j) * SPH_C64(0x0555555555555555)) - -#if SPH_SMALL_FOOTPRINT_BMW - -static const sph_u64 Kb_tab[] = { - Kb(16), Kb(17), Kb(18), Kb(19), Kb(20), Kb(21), Kb(22), Kb(23), - Kb(24), Kb(25), Kb(26), Kb(27), Kb(28), Kb(29), Kb(30), Kb(31) -}; - -#define rol_off(mf, j, off) \ - SPH_ROTL64(mf(((j) + (off)) & 15), (((j) + (off)) & 15) + 1) - -#define add_elt_b(mf, hf, j) \ - (SPH_T64(rol_off(mf, j, 0) + rol_off(mf, j, 3) \ - - rol_off(mf, j, 10) + Kb_tab[j]) ^ hf(((j) + 7) & 15)) - -#define expand1b(qf, mf, hf, i) \ - SPH_T64(sb1(qf((i) - 16)) + sb2(qf((i) - 15)) \ - + sb3(qf((i) - 14)) + sb0(qf((i) - 13)) \ - + sb1(qf((i) - 12)) + sb2(qf((i) - 11)) \ - + sb3(qf((i) - 10)) + sb0(qf((i) - 9)) \ - + sb1(qf((i) - 8)) + sb2(qf((i) - 7)) \ - + sb3(qf((i) - 6)) + sb0(qf((i) - 5)) \ - + sb1(qf((i) - 4)) + sb2(qf((i) - 3)) \ - + sb3(qf((i) - 2)) + sb0(qf((i) - 1)) \ - + add_elt_b(mf, hf, (i) - 16)) - -#define expand2b(qf, mf, hf, i) \ - SPH_T64(qf((i) - 16) + rb1(qf((i) - 15)) \ - + qf((i) - 14) + rb2(qf((i) - 13)) \ - + qf((i) - 12) + rb3(qf((i) - 11)) \ - + qf((i) - 10) + rb4(qf((i) - 9)) \ - + qf((i) - 8) + rb5(qf((i) - 7)) \ - + qf((i) - 6) + rb6(qf((i) - 5)) \ - + qf((i) - 4) + rb7(qf((i) - 3)) \ - + sb4(qf((i) - 2)) + sb5(qf((i) - 1)) \ - + add_elt_b(mf, hf, (i) - 16)) - -#else - -#define add_elt_b(mf, hf, j0m, j1m, j3m, j4m, j7m, j10m, j11m, j16) \ - (SPH_T64(SPH_ROTL64(mf(j0m), j1m) + SPH_ROTL64(mf(j3m), j4m) \ - - SPH_ROTL64(mf(j10m), j11m) + Kb(j16)) ^ hf(j7m)) - -#define expand1b_inner(qf, mf, hf, i16, \ - i0, i1, i2, i3, i4, i5, i6, i7, i8, \ - i9, i10, i11, i12, i13, i14, i15, \ - i0m, i1m, i3m, i4m, i7m, i10m, i11m) \ - SPH_T64(sb1(qf(i0)) + sb2(qf(i1)) + sb3(qf(i2)) + sb0(qf(i3)) \ - + sb1(qf(i4)) + sb2(qf(i5)) + sb3(qf(i6)) + sb0(qf(i7)) \ - + sb1(qf(i8)) + sb2(qf(i9)) + sb3(qf(i10)) + sb0(qf(i11)) \ - + sb1(qf(i12)) + sb2(qf(i13)) + sb3(qf(i14)) + sb0(qf(i15)) \ - + add_elt_b(mf, hf, i0m, i1m, i3m, i4m, i7m, i10m, i11m, i16)) - -#define expand1b(qf, mf, hf, i16) \ - expand1b_(qf, mf, hf, i16, I16_ ## i16, M16_ ## i16) -#define expand1b_(qf, mf, hf, i16, ix, iy) \ - expand1b_inner LPAR qf, mf, hf, i16, ix, iy) - -#define expand2b_inner(qf, mf, hf, i16, \ - i0, i1, i2, i3, i4, i5, i6, i7, i8, \ - i9, i10, i11, i12, i13, i14, i15, \ - i0m, i1m, i3m, i4m, i7m, i10m, i11m) \ - SPH_T64(qf(i0) + rb1(qf(i1)) + qf(i2) + rb2(qf(i3)) \ - + qf(i4) + rb3(qf(i5)) + qf(i6) + rb4(qf(i7)) \ - + qf(i8) + rb5(qf(i9)) + qf(i10) + rb6(qf(i11)) \ - + qf(i12) + rb7(qf(i13)) + sb4(qf(i14)) + sb5(qf(i15)) \ - + add_elt_b(mf, hf, i0m, i1m, i3m, i4m, i7m, i10m, i11m, i16)) - -#define expand2b(qf, mf, hf, i16) \ - expand2b_(qf, mf, hf, i16, I16_ ## i16, M16_ ## i16) -#define expand2b_(qf, mf, hf, i16, ix, iy) \ - expand2b_inner LPAR qf, mf, hf, i16, ix, iy) - -#endif - -#endif - -#define MAKE_W(tt, i0, op01, i1, op12, i2, op23, i3, op34, i4) \ - tt((M(i0) ^ H(i0)) op01 (M(i1) ^ H(i1)) op12 (M(i2) ^ H(i2)) \ - op23 (M(i3) ^ H(i3)) op34 (M(i4) ^ H(i4))) - -#define Ws0 MAKE_W(SPH_T32, 5, -, 7, +, 10, +, 13, +, 14) -#define Ws1 MAKE_W(SPH_T32, 6, -, 8, +, 11, +, 14, -, 15) -#define Ws2 MAKE_W(SPH_T32, 0, +, 7, +, 9, -, 12, +, 15) -#define Ws3 MAKE_W(SPH_T32, 0, -, 1, +, 8, -, 10, +, 13) -#define Ws4 MAKE_W(SPH_T32, 1, +, 2, +, 9, -, 11, -, 14) -#define Ws5 MAKE_W(SPH_T32, 3, -, 2, +, 10, -, 12, +, 15) -#define Ws6 MAKE_W(SPH_T32, 4, -, 0, -, 3, -, 11, +, 13) -#define Ws7 MAKE_W(SPH_T32, 1, -, 4, -, 5, -, 12, -, 14) -#define Ws8 MAKE_W(SPH_T32, 2, -, 5, -, 6, +, 13, -, 15) -#define Ws9 MAKE_W(SPH_T32, 0, -, 3, +, 6, -, 7, +, 14) -#define Ws10 MAKE_W(SPH_T32, 8, -, 1, -, 4, -, 7, +, 15) -#define Ws11 MAKE_W(SPH_T32, 8, -, 0, -, 2, -, 5, +, 9) -#define Ws12 MAKE_W(SPH_T32, 1, +, 3, -, 6, -, 9, +, 10) -#define Ws13 MAKE_W(SPH_T32, 2, +, 4, +, 7, +, 10, +, 11) -#define Ws14 MAKE_W(SPH_T32, 3, -, 5, +, 8, -, 11, -, 12) -#define Ws15 MAKE_W(SPH_T32, 12, -, 4, -, 6, -, 9, +, 13) - -#if SPH_SMALL_FOOTPRINT_BMW - -#define MAKE_Qas do { \ - unsigned u; \ - sph_u32 Ws[16]; \ - Ws[ 0] = Ws0; \ - Ws[ 1] = Ws1; \ - Ws[ 2] = Ws2; \ - Ws[ 3] = Ws3; \ - Ws[ 4] = Ws4; \ - Ws[ 5] = Ws5; \ - Ws[ 6] = Ws6; \ - Ws[ 7] = Ws7; \ - Ws[ 8] = Ws8; \ - Ws[ 9] = Ws9; \ - Ws[10] = Ws10; \ - Ws[11] = Ws11; \ - Ws[12] = Ws12; \ - Ws[13] = Ws13; \ - Ws[14] = Ws14; \ - Ws[15] = Ws15; \ - for (u = 0; u < 15; u += 5) { \ - qt[u + 0] = SPH_T32(ss0(Ws[u + 0]) + H(u + 1)); \ - qt[u + 1] = SPH_T32(ss1(Ws[u + 1]) + H(u + 2)); \ - qt[u + 2] = SPH_T32(ss2(Ws[u + 2]) + H(u + 3)); \ - qt[u + 3] = SPH_T32(ss3(Ws[u + 3]) + H(u + 4)); \ - qt[u + 4] = SPH_T32(ss4(Ws[u + 4]) + H(u + 5)); \ - } \ - qt[15] = SPH_T32(ss0(Ws[15]) + H(0)); \ - } while (0) - -#define MAKE_Qbs do { \ - qt[16] = expand1s(Qs, M, H, 16); \ - qt[17] = expand1s(Qs, M, H, 17); \ - qt[18] = expand2s(Qs, M, H, 18); \ - qt[19] = expand2s(Qs, M, H, 19); \ - qt[20] = expand2s(Qs, M, H, 20); \ - qt[21] = expand2s(Qs, M, H, 21); \ - qt[22] = expand2s(Qs, M, H, 22); \ - qt[23] = expand2s(Qs, M, H, 23); \ - qt[24] = expand2s(Qs, M, H, 24); \ - qt[25] = expand2s(Qs, M, H, 25); \ - qt[26] = expand2s(Qs, M, H, 26); \ - qt[27] = expand2s(Qs, M, H, 27); \ - qt[28] = expand2s(Qs, M, H, 28); \ - qt[29] = expand2s(Qs, M, H, 29); \ - qt[30] = expand2s(Qs, M, H, 30); \ - qt[31] = expand2s(Qs, M, H, 31); \ - } while (0) - -#else - -#define MAKE_Qas do { \ - qt[ 0] = SPH_T32(ss0(Ws0 ) + H( 1)); \ - qt[ 1] = SPH_T32(ss1(Ws1 ) + H( 2)); \ - qt[ 2] = SPH_T32(ss2(Ws2 ) + H( 3)); \ - qt[ 3] = SPH_T32(ss3(Ws3 ) + H( 4)); \ - qt[ 4] = SPH_T32(ss4(Ws4 ) + H( 5)); \ - qt[ 5] = SPH_T32(ss0(Ws5 ) + H( 6)); \ - qt[ 6] = SPH_T32(ss1(Ws6 ) + H( 7)); \ - qt[ 7] = SPH_T32(ss2(Ws7 ) + H( 8)); \ - qt[ 8] = SPH_T32(ss3(Ws8 ) + H( 9)); \ - qt[ 9] = SPH_T32(ss4(Ws9 ) + H(10)); \ - qt[10] = SPH_T32(ss0(Ws10) + H(11)); \ - qt[11] = SPH_T32(ss1(Ws11) + H(12)); \ - qt[12] = SPH_T32(ss2(Ws12) + H(13)); \ - qt[13] = SPH_T32(ss3(Ws13) + H(14)); \ - qt[14] = SPH_T32(ss4(Ws14) + H(15)); \ - qt[15] = SPH_T32(ss0(Ws15) + H( 0)); \ - } while (0) - -#define MAKE_Qbs do { \ - qt[16] = expand1s(Qs, M, H, 16); \ - qt[17] = expand1s(Qs, M, H, 17); \ - qt[18] = expand2s(Qs, M, H, 18); \ - qt[19] = expand2s(Qs, M, H, 19); \ - qt[20] = expand2s(Qs, M, H, 20); \ - qt[21] = expand2s(Qs, M, H, 21); \ - qt[22] = expand2s(Qs, M, H, 22); \ - qt[23] = expand2s(Qs, M, H, 23); \ - qt[24] = expand2s(Qs, M, H, 24); \ - qt[25] = expand2s(Qs, M, H, 25); \ - qt[26] = expand2s(Qs, M, H, 26); \ - qt[27] = expand2s(Qs, M, H, 27); \ - qt[28] = expand2s(Qs, M, H, 28); \ - qt[29] = expand2s(Qs, M, H, 29); \ - qt[30] = expand2s(Qs, M, H, 30); \ - qt[31] = expand2s(Qs, M, H, 31); \ - } while (0) - -#endif - -#define MAKE_Qs do { \ - MAKE_Qas; \ - MAKE_Qbs; \ - } while (0) - -#define Qs(j) (qt[j]) - -#if SPH_64 - -#define Wb0 MAKE_W(SPH_T64, 5, -, 7, +, 10, +, 13, +, 14) -#define Wb1 MAKE_W(SPH_T64, 6, -, 8, +, 11, +, 14, -, 15) -#define Wb2 MAKE_W(SPH_T64, 0, +, 7, +, 9, -, 12, +, 15) -#define Wb3 MAKE_W(SPH_T64, 0, -, 1, +, 8, -, 10, +, 13) -#define Wb4 MAKE_W(SPH_T64, 1, +, 2, +, 9, -, 11, -, 14) -#define Wb5 MAKE_W(SPH_T64, 3, -, 2, +, 10, -, 12, +, 15) -#define Wb6 MAKE_W(SPH_T64, 4, -, 0, -, 3, -, 11, +, 13) -#define Wb7 MAKE_W(SPH_T64, 1, -, 4, -, 5, -, 12, -, 14) -#define Wb8 MAKE_W(SPH_T64, 2, -, 5, -, 6, +, 13, -, 15) -#define Wb9 MAKE_W(SPH_T64, 0, -, 3, +, 6, -, 7, +, 14) -#define Wb10 MAKE_W(SPH_T64, 8, -, 1, -, 4, -, 7, +, 15) -#define Wb11 MAKE_W(SPH_T64, 8, -, 0, -, 2, -, 5, +, 9) -#define Wb12 MAKE_W(SPH_T64, 1, +, 3, -, 6, -, 9, +, 10) -#define Wb13 MAKE_W(SPH_T64, 2, +, 4, +, 7, +, 10, +, 11) -#define Wb14 MAKE_W(SPH_T64, 3, -, 5, +, 8, -, 11, -, 12) -#define Wb15 MAKE_W(SPH_T64, 12, -, 4, -, 6, -, 9, +, 13) - -#if SPH_SMALL_FOOTPRINT_BMW - -#define MAKE_Qab do { \ - unsigned u; \ - sph_u64 Wb[16]; \ - Wb[ 0] = Wb0; \ - Wb[ 1] = Wb1; \ - Wb[ 2] = Wb2; \ - Wb[ 3] = Wb3; \ - Wb[ 4] = Wb4; \ - Wb[ 5] = Wb5; \ - Wb[ 6] = Wb6; \ - Wb[ 7] = Wb7; \ - Wb[ 8] = Wb8; \ - Wb[ 9] = Wb9; \ - Wb[10] = Wb10; \ - Wb[11] = Wb11; \ - Wb[12] = Wb12; \ - Wb[13] = Wb13; \ - Wb[14] = Wb14; \ - Wb[15] = Wb15; \ - for (u = 0; u < 15; u += 5) { \ - qt[u + 0] = SPH_T64(sb0(Wb[u + 0]) + H(u + 1)); \ - qt[u + 1] = SPH_T64(sb1(Wb[u + 1]) + H(u + 2)); \ - qt[u + 2] = SPH_T64(sb2(Wb[u + 2]) + H(u + 3)); \ - qt[u + 3] = SPH_T64(sb3(Wb[u + 3]) + H(u + 4)); \ - qt[u + 4] = SPH_T64(sb4(Wb[u + 4]) + H(u + 5)); \ - } \ - qt[15] = SPH_T64(sb0(Wb[15]) + H(0)); \ - } while (0) - -#define MAKE_Qbb do { \ - unsigned u; \ - for (u = 16; u < 18; u ++) \ - qt[u] = expand1b(Qb, M, H, u); \ - for (u = 18; u < 32; u ++) \ - qt[u] = expand2b(Qb, M, H, u); \ - } while (0) - -#else - -#define MAKE_Qab do { \ - qt[ 0] = SPH_T64(sb0(Wb0 ) + H( 1)); \ - qt[ 1] = SPH_T64(sb1(Wb1 ) + H( 2)); \ - qt[ 2] = SPH_T64(sb2(Wb2 ) + H( 3)); \ - qt[ 3] = SPH_T64(sb3(Wb3 ) + H( 4)); \ - qt[ 4] = SPH_T64(sb4(Wb4 ) + H( 5)); \ - qt[ 5] = SPH_T64(sb0(Wb5 ) + H( 6)); \ - qt[ 6] = SPH_T64(sb1(Wb6 ) + H( 7)); \ - qt[ 7] = SPH_T64(sb2(Wb7 ) + H( 8)); \ - qt[ 8] = SPH_T64(sb3(Wb8 ) + H( 9)); \ - qt[ 9] = SPH_T64(sb4(Wb9 ) + H(10)); \ - qt[10] = SPH_T64(sb0(Wb10) + H(11)); \ - qt[11] = SPH_T64(sb1(Wb11) + H(12)); \ - qt[12] = SPH_T64(sb2(Wb12) + H(13)); \ - qt[13] = SPH_T64(sb3(Wb13) + H(14)); \ - qt[14] = SPH_T64(sb4(Wb14) + H(15)); \ - qt[15] = SPH_T64(sb0(Wb15) + H( 0)); \ - } while (0) - -#define MAKE_Qbb do { \ - qt[16] = expand1b(Qb, M, H, 16); \ - qt[17] = expand1b(Qb, M, H, 17); \ - qt[18] = expand2b(Qb, M, H, 18); \ - qt[19] = expand2b(Qb, M, H, 19); \ - qt[20] = expand2b(Qb, M, H, 20); \ - qt[21] = expand2b(Qb, M, H, 21); \ - qt[22] = expand2b(Qb, M, H, 22); \ - qt[23] = expand2b(Qb, M, H, 23); \ - qt[24] = expand2b(Qb, M, H, 24); \ - qt[25] = expand2b(Qb, M, H, 25); \ - qt[26] = expand2b(Qb, M, H, 26); \ - qt[27] = expand2b(Qb, M, H, 27); \ - qt[28] = expand2b(Qb, M, H, 28); \ - qt[29] = expand2b(Qb, M, H, 29); \ - qt[30] = expand2b(Qb, M, H, 30); \ - qt[31] = expand2b(Qb, M, H, 31); \ - } while (0) - -#endif - -#define MAKE_Qb do { \ - MAKE_Qab; \ - MAKE_Qbb; \ - } while (0) - -#define Qb(j) (qt[j]) - -#endif - -#define FOLD(type, mkQ, tt, rol, mf, qf, dhf) do { \ - type qt[32], xl, xh; \ - mkQ; \ - xl = qf(16) ^ qf(17) ^ qf(18) ^ qf(19) \ - ^ qf(20) ^ qf(21) ^ qf(22) ^ qf(23); \ - xh = xl ^ qf(24) ^ qf(25) ^ qf(26) ^ qf(27) \ - ^ qf(28) ^ qf(29) ^ qf(30) ^ qf(31); \ - dhf( 0) = tt(((xh << 5) ^ (qf(16) >> 5) ^ mf( 0)) \ - + (xl ^ qf(24) ^ qf( 0))); \ - dhf( 1) = tt(((xh >> 7) ^ (qf(17) << 8) ^ mf( 1)) \ - + (xl ^ qf(25) ^ qf( 1))); \ - dhf( 2) = tt(((xh >> 5) ^ (qf(18) << 5) ^ mf( 2)) \ - + (xl ^ qf(26) ^ qf( 2))); \ - dhf( 3) = tt(((xh >> 1) ^ (qf(19) << 5) ^ mf( 3)) \ - + (xl ^ qf(27) ^ qf( 3))); \ - dhf( 4) = tt(((xh >> 3) ^ (qf(20) << 0) ^ mf( 4)) \ - + (xl ^ qf(28) ^ qf( 4))); \ - dhf( 5) = tt(((xh << 6) ^ (qf(21) >> 6) ^ mf( 5)) \ - + (xl ^ qf(29) ^ qf( 5))); \ - dhf( 6) = tt(((xh >> 4) ^ (qf(22) << 6) ^ mf( 6)) \ - + (xl ^ qf(30) ^ qf( 6))); \ - dhf( 7) = tt(((xh >> 11) ^ (qf(23) << 2) ^ mf( 7)) \ - + (xl ^ qf(31) ^ qf( 7))); \ - dhf( 8) = tt(rol(dhf(4), 9) + (xh ^ qf(24) ^ mf( 8)) \ - + ((xl << 8) ^ qf(23) ^ qf( 8))); \ - dhf( 9) = tt(rol(dhf(5), 10) + (xh ^ qf(25) ^ mf( 9)) \ - + ((xl >> 6) ^ qf(16) ^ qf( 9))); \ - dhf(10) = tt(rol(dhf(6), 11) + (xh ^ qf(26) ^ mf(10)) \ - + ((xl << 6) ^ qf(17) ^ qf(10))); \ - dhf(11) = tt(rol(dhf(7), 12) + (xh ^ qf(27) ^ mf(11)) \ - + ((xl << 4) ^ qf(18) ^ qf(11))); \ - dhf(12) = tt(rol(dhf(0), 13) + (xh ^ qf(28) ^ mf(12)) \ - + ((xl >> 3) ^ qf(19) ^ qf(12))); \ - dhf(13) = tt(rol(dhf(1), 14) + (xh ^ qf(29) ^ mf(13)) \ - + ((xl >> 4) ^ qf(20) ^ qf(13))); \ - dhf(14) = tt(rol(dhf(2), 15) + (xh ^ qf(30) ^ mf(14)) \ - + ((xl >> 7) ^ qf(21) ^ qf(14))); \ - dhf(15) = tt(rol(dhf(3), 16) + (xh ^ qf(31) ^ mf(15)) \ - + ((xl >> 2) ^ qf(22) ^ qf(15))); \ - } while (0) - -#define FOLDs FOLD(sph_u32, MAKE_Qs, SPH_T32, SPH_ROTL32, M, Qs, dH) - -#if SPH_64 - -#define FOLDb FOLD(sph_u64, MAKE_Qb, SPH_T64, SPH_ROTL64, M, Qb, dH) - -#endif - -static void -compress_small(const unsigned char *data, const sph_u32 h[16], sph_u32 dh[16]) -{ -#if SPH_LITTLE_FAST -#define M(x) sph_dec32le_aligned(data + 4 * (x)) -#else - sph_u32 mv[16]; - - mv[ 0] = sph_dec32le_aligned(data + 0); - mv[ 1] = sph_dec32le_aligned(data + 4); - mv[ 2] = sph_dec32le_aligned(data + 8); - mv[ 3] = sph_dec32le_aligned(data + 12); - mv[ 4] = sph_dec32le_aligned(data + 16); - mv[ 5] = sph_dec32le_aligned(data + 20); - mv[ 6] = sph_dec32le_aligned(data + 24); - mv[ 7] = sph_dec32le_aligned(data + 28); - mv[ 8] = sph_dec32le_aligned(data + 32); - mv[ 9] = sph_dec32le_aligned(data + 36); - mv[10] = sph_dec32le_aligned(data + 40); - mv[11] = sph_dec32le_aligned(data + 44); - mv[12] = sph_dec32le_aligned(data + 48); - mv[13] = sph_dec32le_aligned(data + 52); - mv[14] = sph_dec32le_aligned(data + 56); - mv[15] = sph_dec32le_aligned(data + 60); -#define M(x) (mv[x]) -#endif -#define H(x) (h[x]) -#define dH(x) (dh[x]) - - FOLDs; - -#undef M -#undef H -#undef dH -} - -static const sph_u32 final_s[16] = { - SPH_C32(0xaaaaaaa0), SPH_C32(0xaaaaaaa1), SPH_C32(0xaaaaaaa2), - SPH_C32(0xaaaaaaa3), SPH_C32(0xaaaaaaa4), SPH_C32(0xaaaaaaa5), - SPH_C32(0xaaaaaaa6), SPH_C32(0xaaaaaaa7), SPH_C32(0xaaaaaaa8), - SPH_C32(0xaaaaaaa9), SPH_C32(0xaaaaaaaa), SPH_C32(0xaaaaaaab), - SPH_C32(0xaaaaaaac), SPH_C32(0xaaaaaaad), SPH_C32(0xaaaaaaae), - SPH_C32(0xaaaaaaaf) -}; - -static void -bmw32_init(sph_bmw_small_context *sc, const sph_u32 *iv) -{ - memcpy(sc->H, iv, sizeof sc->H); - sc->ptr = 0; -#if SPH_64 - sc->bit_count = 0; -#else - sc->bit_count_high = 0; - sc->bit_count_low = 0; -#endif -} - -static void -bmw32(sph_bmw_small_context *sc, const void *data, size_t len) -{ - unsigned char *buf; - size_t ptr; - sph_u32 htmp[16]; - sph_u32 *h1, *h2; -#if !SPH_64 - sph_u32 tmp; -#endif - -#if SPH_64 - sc->bit_count += (sph_u64)len << 3; -#else - tmp = sc->bit_count_low; - sc->bit_count_low = SPH_T32(tmp + ((sph_u32)len << 3)); - if (sc->bit_count_low < tmp) - sc->bit_count_high ++; - sc->bit_count_high += len >> 29; -#endif - buf = sc->buf; - ptr = sc->ptr; - h1 = sc->H; - h2 = htmp; - while (len > 0) { - size_t clen; - - clen = (sizeof sc->buf) - ptr; - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - data = (const unsigned char *)data + clen; - len -= clen; - ptr += clen; - if (ptr == sizeof sc->buf) { - sph_u32 *ht; - - compress_small(buf, h1, h2); - ht = h1; - h1 = h2; - h2 = ht; - ptr = 0; - } - } - sc->ptr = ptr; - if (h1 != sc->H) - memcpy(sc->H, h1, sizeof sc->H); -} - -static void -bmw32_close(sph_bmw_small_context *sc, unsigned ub, unsigned n, - void *dst, size_t out_size_w32) -{ - unsigned char *buf, *out; - size_t ptr, u, v; - unsigned z; - sph_u32 h1[16], h2[16], *h; - - buf = sc->buf; - ptr = sc->ptr; - z = 0x80 >> n; - buf[ptr ++] = ((ub & -z) | z) & 0xFF; - h = sc->H; - if (ptr > (sizeof sc->buf) - 8) { - memset(buf + ptr, 0, (sizeof sc->buf) - ptr); - compress_small(buf, h, h1); - ptr = 0; - h = h1; - } - memset(buf + ptr, 0, (sizeof sc->buf) - 8 - ptr); -#if SPH_64 - sph_enc64le_aligned(buf + (sizeof sc->buf) - 8, - SPH_T64(sc->bit_count + n)); -#else - sph_enc32le_aligned(buf + (sizeof sc->buf) - 8, - sc->bit_count_low + n); - sph_enc32le_aligned(buf + (sizeof sc->buf) - 4, - SPH_T32(sc->bit_count_high)); -#endif - compress_small(buf, h, h2); - for (u = 0; u < 16; u ++) - sph_enc32le_aligned(buf + 4 * u, h2[u]); - compress_small(buf, final_s, h1); - out = dst; - for (u = 0, v = 16 - out_size_w32; u < out_size_w32; u ++, v ++) - sph_enc32le(out + 4 * u, h1[v]); -} - -#if SPH_64 - -static void -compress_big(const unsigned char *data, const sph_u64 h[16], sph_u64 dh[16]) -{ -#if SPH_LITTLE_FAST -#define M(x) sph_dec64le_aligned(data + 8 * (x)) -#else - sph_u64 mv[16]; - - mv[ 0] = sph_dec64le_aligned(data + 0); - mv[ 1] = sph_dec64le_aligned(data + 8); - mv[ 2] = sph_dec64le_aligned(data + 16); - mv[ 3] = sph_dec64le_aligned(data + 24); - mv[ 4] = sph_dec64le_aligned(data + 32); - mv[ 5] = sph_dec64le_aligned(data + 40); - mv[ 6] = sph_dec64le_aligned(data + 48); - mv[ 7] = sph_dec64le_aligned(data + 56); - mv[ 8] = sph_dec64le_aligned(data + 64); - mv[ 9] = sph_dec64le_aligned(data + 72); - mv[10] = sph_dec64le_aligned(data + 80); - mv[11] = sph_dec64le_aligned(data + 88); - mv[12] = sph_dec64le_aligned(data + 96); - mv[13] = sph_dec64le_aligned(data + 104); - mv[14] = sph_dec64le_aligned(data + 112); - mv[15] = sph_dec64le_aligned(data + 120); -#define M(x) (mv[x]) -#endif -#define H(x) (h[x]) -#define dH(x) (dh[x]) - - FOLDb; - -#undef M -#undef H -#undef dH -} - -static const sph_u64 final_b[16] = { - SPH_C64(0xaaaaaaaaaaaaaaa0), SPH_C64(0xaaaaaaaaaaaaaaa1), - SPH_C64(0xaaaaaaaaaaaaaaa2), SPH_C64(0xaaaaaaaaaaaaaaa3), - SPH_C64(0xaaaaaaaaaaaaaaa4), SPH_C64(0xaaaaaaaaaaaaaaa5), - SPH_C64(0xaaaaaaaaaaaaaaa6), SPH_C64(0xaaaaaaaaaaaaaaa7), - SPH_C64(0xaaaaaaaaaaaaaaa8), SPH_C64(0xaaaaaaaaaaaaaaa9), - SPH_C64(0xaaaaaaaaaaaaaaaa), SPH_C64(0xaaaaaaaaaaaaaaab), - SPH_C64(0xaaaaaaaaaaaaaaac), SPH_C64(0xaaaaaaaaaaaaaaad), - SPH_C64(0xaaaaaaaaaaaaaaae), SPH_C64(0xaaaaaaaaaaaaaaaf) -}; - -static void -bmw64_init(sph_bmw_big_context *sc, const sph_u64 *iv) -{ - memcpy(sc->H, iv, sizeof sc->H); - sc->ptr = 0; - sc->bit_count = 0; -} - -static void -bmw64(sph_bmw_big_context *sc, const void *data, size_t len) -{ - unsigned char *buf; - size_t ptr; - sph_u64 htmp[16]; - sph_u64 *h1, *h2; - - sc->bit_count += (sph_u64)len << 3; - buf = sc->buf; - ptr = sc->ptr; - h1 = sc->H; - h2 = htmp; - while (len > 0) { - size_t clen; - - clen = (sizeof sc->buf) - ptr; - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - data = (const unsigned char *)data + clen; - len -= clen; - ptr += clen; - if (ptr == sizeof sc->buf) { - sph_u64 *ht; - - compress_big(buf, h1, h2); - ht = h1; - h1 = h2; - h2 = ht; - ptr = 0; - } - } - sc->ptr = ptr; - if (h1 != sc->H) - memcpy(sc->H, h1, sizeof sc->H); -} - -static void -bmw64_close(sph_bmw_big_context *sc, unsigned ub, unsigned n, - void *dst, size_t out_size_w64) -{ - unsigned char *buf, *out; - size_t ptr, u, v; - unsigned z; - sph_u64 h1[16], h2[16], *h; - - buf = sc->buf; - ptr = sc->ptr; - z = 0x80 >> n; - buf[ptr ++] = ((ub & -z) | z) & 0xFF; - h = sc->H; - if (ptr > (sizeof sc->buf) - 8) { - memset(buf + ptr, 0, (sizeof sc->buf) - ptr); - compress_big(buf, h, h1); - ptr = 0; - h = h1; - } - memset(buf + ptr, 0, (sizeof sc->buf) - 8 - ptr); - sph_enc64le_aligned(buf + (sizeof sc->buf) - 8, - SPH_T64(sc->bit_count + n)); - compress_big(buf, h, h2); - for (u = 0; u < 16; u ++) - sph_enc64le_aligned(buf + 8 * u, h2[u]); - compress_big(buf, final_b, h1); - out = dst; - for (u = 0, v = 16 - out_size_w64; u < out_size_w64; u ++, v ++) - sph_enc64le(out + 8 * u, h1[v]); -} - -#endif - -/* see sph_bmw.h */ -void -sph_bmw224_init(void *cc) -{ - bmw32_init(cc, IV224); -} - -/* see sph_bmw.h */ -void -sph_bmw224(void *cc, const void *data, size_t len) -{ - bmw32(cc, data, len); -} - -/* see sph_bmw.h */ -void -sph_bmw224_close(void *cc, void *dst) -{ - sph_bmw224_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_bmw.h */ -void -sph_bmw224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - bmw32_close(cc, ub, n, dst, 7); - sph_bmw224_init(cc); -} - -/* see sph_bmw.h */ -void -sph_bmw256_init(void *cc) -{ - bmw32_init(cc, IV256); -} - -/* see sph_bmw.h */ -void -sph_bmw256(void *cc, const void *data, size_t len) -{ - bmw32(cc, data, len); -} - -/* see sph_bmw.h */ -void -sph_bmw256_close(void *cc, void *dst) -{ - sph_bmw256_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_bmw.h */ -void -sph_bmw256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - bmw32_close(cc, ub, n, dst, 8); - sph_bmw256_init(cc); -} - -#if SPH_64 - -/* see sph_bmw.h */ -void -sph_bmw384_init(void *cc) -{ - bmw64_init(cc, IV384); -} - -/* see sph_bmw.h */ -void -sph_bmw384(void *cc, const void *data, size_t len) -{ - bmw64(cc, data, len); -} - -/* see sph_bmw.h */ -void -sph_bmw384_close(void *cc, void *dst) -{ - sph_bmw384_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_bmw.h */ -void -sph_bmw384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - bmw64_close(cc, ub, n, dst, 6); - sph_bmw384_init(cc); -} - -/* see sph_bmw.h */ -void -sph_bmw512_init(void *cc) -{ - bmw64_init(cc, IV512); -} - -/* see sph_bmw.h */ -void -sph_bmw512(void *cc, const void *data, size_t len) -{ - bmw64(cc, data, len); -} - -/* see sph_bmw.h */ -void -sph_bmw512_close(void *cc, void *dst) -{ - sph_bmw512_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_bmw.h */ -void -sph_bmw512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - bmw64_close(cc, ub, n, dst, 8); - sph_bmw512_init(cc); -} - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/src/crypto/cubehash.c b/src/crypto/cubehash.c deleted file mode 100644 index 9322fe14e055..000000000000 --- a/src/crypto/cubehash.c +++ /dev/null @@ -1,723 +0,0 @@ -/* $Id: cubehash.c 227 2010-06-16 17:28:38Z tp $ */ -/* - * CubeHash implementation. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @author Thomas Pornin - */ - -#include -#include -#include - -#include "sph_cubehash.h" -#ifdef __cplusplus -extern "C"{ -#endif - -#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_CUBEHASH -#define SPH_SMALL_FOOTPRINT_CUBEHASH 1 -#endif - -/* - * Some tests were conducted on an Intel Core2 Q6600 (32-bit and 64-bit - * mode), a PowerPC G3, and a MIPS-compatible CPU (Broadcom BCM3302). - * It appears that the optimal settings are: - * -- full unroll, no state copy on the "big" systems (x86, PowerPC) - * -- unroll to 4 or 8, state copy on the "small" system (MIPS) - */ - -#if SPH_SMALL_FOOTPRINT_CUBEHASH - -#if !defined SPH_CUBEHASH_UNROLL -#define SPH_CUBEHASH_UNROLL 4 -#endif -#if !defined SPH_CUBEHASH_NOCOPY -#define SPH_CUBEHASH_NOCOPY 1 -#endif - -#else - -#if !defined SPH_CUBEHASH_UNROLL -#define SPH_CUBEHASH_UNROLL 0 -#endif -#if !defined SPH_CUBEHASH_NOCOPY -#define SPH_CUBEHASH_NOCOPY 0 -#endif - -#endif - -#ifdef _MSC_VER -#pragma warning (disable: 4146) -#endif - -static const sph_u32 IV224[] = { - SPH_C32(0xB0FC8217), SPH_C32(0x1BEE1A90), SPH_C32(0x829E1A22), - SPH_C32(0x6362C342), SPH_C32(0x24D91C30), SPH_C32(0x03A7AA24), - SPH_C32(0xA63721C8), SPH_C32(0x85B0E2EF), SPH_C32(0xF35D13F3), - SPH_C32(0x41DA807D), SPH_C32(0x21A70CA6), SPH_C32(0x1F4E9774), - SPH_C32(0xB3E1C932), SPH_C32(0xEB0A79A8), SPH_C32(0xCDDAAA66), - SPH_C32(0xE2F6ECAA), SPH_C32(0x0A713362), SPH_C32(0xAA3080E0), - SPH_C32(0xD8F23A32), SPH_C32(0xCEF15E28), SPH_C32(0xDB086314), - SPH_C32(0x7F709DF7), SPH_C32(0xACD228A4), SPH_C32(0x704D6ECE), - SPH_C32(0xAA3EC95F), SPH_C32(0xE387C214), SPH_C32(0x3A6445FF), - SPH_C32(0x9CAB81C3), SPH_C32(0xC73D4B98), SPH_C32(0xD277AEBE), - SPH_C32(0xFD20151C), SPH_C32(0x00CB573E) -}; - -static const sph_u32 IV256[] = { - SPH_C32(0xEA2BD4B4), SPH_C32(0xCCD6F29F), SPH_C32(0x63117E71), - SPH_C32(0x35481EAE), SPH_C32(0x22512D5B), SPH_C32(0xE5D94E63), - SPH_C32(0x7E624131), SPH_C32(0xF4CC12BE), SPH_C32(0xC2D0B696), - SPH_C32(0x42AF2070), SPH_C32(0xD0720C35), SPH_C32(0x3361DA8C), - SPH_C32(0x28CCECA4), SPH_C32(0x8EF8AD83), SPH_C32(0x4680AC00), - SPH_C32(0x40E5FBAB), SPH_C32(0xD89041C3), SPH_C32(0x6107FBD5), - SPH_C32(0x6C859D41), SPH_C32(0xF0B26679), SPH_C32(0x09392549), - SPH_C32(0x5FA25603), SPH_C32(0x65C892FD), SPH_C32(0x93CB6285), - SPH_C32(0x2AF2B5AE), SPH_C32(0x9E4B4E60), SPH_C32(0x774ABFDD), - SPH_C32(0x85254725), SPH_C32(0x15815AEB), SPH_C32(0x4AB6AAD6), - SPH_C32(0x9CDAF8AF), SPH_C32(0xD6032C0A) -}; - -static const sph_u32 IV384[] = { - SPH_C32(0xE623087E), SPH_C32(0x04C00C87), SPH_C32(0x5EF46453), - SPH_C32(0x69524B13), SPH_C32(0x1A05C7A9), SPH_C32(0x3528DF88), - SPH_C32(0x6BDD01B5), SPH_C32(0x5057B792), SPH_C32(0x6AA7A922), - SPH_C32(0x649C7EEE), SPH_C32(0xF426309F), SPH_C32(0xCB629052), - SPH_C32(0xFC8E20ED), SPH_C32(0xB3482BAB), SPH_C32(0xF89E5E7E), - SPH_C32(0xD83D4DE4), SPH_C32(0x44BFC10D), SPH_C32(0x5FC1E63D), - SPH_C32(0x2104E6CB), SPH_C32(0x17958F7F), SPH_C32(0xDBEAEF70), - SPH_C32(0xB4B97E1E), SPH_C32(0x32C195F6), SPH_C32(0x6184A8E4), - SPH_C32(0x796C2543), SPH_C32(0x23DE176D), SPH_C32(0xD33BBAEC), - SPH_C32(0x0C12E5D2), SPH_C32(0x4EB95A7B), SPH_C32(0x2D18BA01), - SPH_C32(0x04EE475F), SPH_C32(0x1FC5F22E) -}; - -static const sph_u32 IV512[] = { - SPH_C32(0x2AEA2A61), SPH_C32(0x50F494D4), SPH_C32(0x2D538B8B), - SPH_C32(0x4167D83E), SPH_C32(0x3FEE2313), SPH_C32(0xC701CF8C), - SPH_C32(0xCC39968E), SPH_C32(0x50AC5695), SPH_C32(0x4D42C787), - SPH_C32(0xA647A8B3), SPH_C32(0x97CF0BEF), SPH_C32(0x825B4537), - SPH_C32(0xEEF864D2), SPH_C32(0xF22090C4), SPH_C32(0xD0E5CD33), - SPH_C32(0xA23911AE), SPH_C32(0xFCD398D9), SPH_C32(0x148FE485), - SPH_C32(0x1B017BEF), SPH_C32(0xB6444532), SPH_C32(0x6A536159), - SPH_C32(0x2FF5781C), SPH_C32(0x91FA7934), SPH_C32(0x0DBADEA9), - SPH_C32(0xD65C8A2B), SPH_C32(0xA5A70E75), SPH_C32(0xB1C62456), - SPH_C32(0xBC796576), SPH_C32(0x1921C8F7), SPH_C32(0xE7989AF1), - SPH_C32(0x7795D246), SPH_C32(0xD43E3B44) -}; - -#define T32 SPH_T32 -#define ROTL32 SPH_ROTL32 - -#if SPH_CUBEHASH_NOCOPY - -#define DECL_STATE -#define READ_STATE(cc) -#define WRITE_STATE(cc) - -#define x0 ((sc)->state[ 0]) -#define x1 ((sc)->state[ 1]) -#define x2 ((sc)->state[ 2]) -#define x3 ((sc)->state[ 3]) -#define x4 ((sc)->state[ 4]) -#define x5 ((sc)->state[ 5]) -#define x6 ((sc)->state[ 6]) -#define x7 ((sc)->state[ 7]) -#define x8 ((sc)->state[ 8]) -#define x9 ((sc)->state[ 9]) -#define xa ((sc)->state[10]) -#define xb ((sc)->state[11]) -#define xc ((sc)->state[12]) -#define xd ((sc)->state[13]) -#define xe ((sc)->state[14]) -#define xf ((sc)->state[15]) -#define xg ((sc)->state[16]) -#define xh ((sc)->state[17]) -#define xi ((sc)->state[18]) -#define xj ((sc)->state[19]) -#define xk ((sc)->state[20]) -#define xl ((sc)->state[21]) -#define xm ((sc)->state[22]) -#define xn ((sc)->state[23]) -#define xo ((sc)->state[24]) -#define xp ((sc)->state[25]) -#define xq ((sc)->state[26]) -#define xr ((sc)->state[27]) -#define xs ((sc)->state[28]) -#define xt ((sc)->state[29]) -#define xu ((sc)->state[30]) -#define xv ((sc)->state[31]) - -#else - -#define DECL_STATE \ - sph_u32 x0, x1, x2, x3, x4, x5, x6, x7; \ - sph_u32 x8, x9, xa, xb, xc, xd, xe, xf; \ - sph_u32 xg, xh, xi, xj, xk, xl, xm, xn; \ - sph_u32 xo, xp, xq, xr, xs, xt, xu, xv; - -#define READ_STATE(cc) do { \ - x0 = (cc)->state[ 0]; \ - x1 = (cc)->state[ 1]; \ - x2 = (cc)->state[ 2]; \ - x3 = (cc)->state[ 3]; \ - x4 = (cc)->state[ 4]; \ - x5 = (cc)->state[ 5]; \ - x6 = (cc)->state[ 6]; \ - x7 = (cc)->state[ 7]; \ - x8 = (cc)->state[ 8]; \ - x9 = (cc)->state[ 9]; \ - xa = (cc)->state[10]; \ - xb = (cc)->state[11]; \ - xc = (cc)->state[12]; \ - xd = (cc)->state[13]; \ - xe = (cc)->state[14]; \ - xf = (cc)->state[15]; \ - xg = (cc)->state[16]; \ - xh = (cc)->state[17]; \ - xi = (cc)->state[18]; \ - xj = (cc)->state[19]; \ - xk = (cc)->state[20]; \ - xl = (cc)->state[21]; \ - xm = (cc)->state[22]; \ - xn = (cc)->state[23]; \ - xo = (cc)->state[24]; \ - xp = (cc)->state[25]; \ - xq = (cc)->state[26]; \ - xr = (cc)->state[27]; \ - xs = (cc)->state[28]; \ - xt = (cc)->state[29]; \ - xu = (cc)->state[30]; \ - xv = (cc)->state[31]; \ - } while (0) - -#define WRITE_STATE(cc) do { \ - (cc)->state[ 0] = x0; \ - (cc)->state[ 1] = x1; \ - (cc)->state[ 2] = x2; \ - (cc)->state[ 3] = x3; \ - (cc)->state[ 4] = x4; \ - (cc)->state[ 5] = x5; \ - (cc)->state[ 6] = x6; \ - (cc)->state[ 7] = x7; \ - (cc)->state[ 8] = x8; \ - (cc)->state[ 9] = x9; \ - (cc)->state[10] = xa; \ - (cc)->state[11] = xb; \ - (cc)->state[12] = xc; \ - (cc)->state[13] = xd; \ - (cc)->state[14] = xe; \ - (cc)->state[15] = xf; \ - (cc)->state[16] = xg; \ - (cc)->state[17] = xh; \ - (cc)->state[18] = xi; \ - (cc)->state[19] = xj; \ - (cc)->state[20] = xk; \ - (cc)->state[21] = xl; \ - (cc)->state[22] = xm; \ - (cc)->state[23] = xn; \ - (cc)->state[24] = xo; \ - (cc)->state[25] = xp; \ - (cc)->state[26] = xq; \ - (cc)->state[27] = xr; \ - (cc)->state[28] = xs; \ - (cc)->state[29] = xt; \ - (cc)->state[30] = xu; \ - (cc)->state[31] = xv; \ - } while (0) - -#endif - -#define INPUT_BLOCK do { \ - x0 ^= sph_dec32le_aligned(buf + 0); \ - x1 ^= sph_dec32le_aligned(buf + 4); \ - x2 ^= sph_dec32le_aligned(buf + 8); \ - x3 ^= sph_dec32le_aligned(buf + 12); \ - x4 ^= sph_dec32le_aligned(buf + 16); \ - x5 ^= sph_dec32le_aligned(buf + 20); \ - x6 ^= sph_dec32le_aligned(buf + 24); \ - x7 ^= sph_dec32le_aligned(buf + 28); \ - } while (0) - -#define ROUND_EVEN do { \ - xg = T32(x0 + xg); \ - x0 = ROTL32(x0, 7); \ - xh = T32(x1 + xh); \ - x1 = ROTL32(x1, 7); \ - xi = T32(x2 + xi); \ - x2 = ROTL32(x2, 7); \ - xj = T32(x3 + xj); \ - x3 = ROTL32(x3, 7); \ - xk = T32(x4 + xk); \ - x4 = ROTL32(x4, 7); \ - xl = T32(x5 + xl); \ - x5 = ROTL32(x5, 7); \ - xm = T32(x6 + xm); \ - x6 = ROTL32(x6, 7); \ - xn = T32(x7 + xn); \ - x7 = ROTL32(x7, 7); \ - xo = T32(x8 + xo); \ - x8 = ROTL32(x8, 7); \ - xp = T32(x9 + xp); \ - x9 = ROTL32(x9, 7); \ - xq = T32(xa + xq); \ - xa = ROTL32(xa, 7); \ - xr = T32(xb + xr); \ - xb = ROTL32(xb, 7); \ - xs = T32(xc + xs); \ - xc = ROTL32(xc, 7); \ - xt = T32(xd + xt); \ - xd = ROTL32(xd, 7); \ - xu = T32(xe + xu); \ - xe = ROTL32(xe, 7); \ - xv = T32(xf + xv); \ - xf = ROTL32(xf, 7); \ - x8 ^= xg; \ - x9 ^= xh; \ - xa ^= xi; \ - xb ^= xj; \ - xc ^= xk; \ - xd ^= xl; \ - xe ^= xm; \ - xf ^= xn; \ - x0 ^= xo; \ - x1 ^= xp; \ - x2 ^= xq; \ - x3 ^= xr; \ - x4 ^= xs; \ - x5 ^= xt; \ - x6 ^= xu; \ - x7 ^= xv; \ - xi = T32(x8 + xi); \ - x8 = ROTL32(x8, 11); \ - xj = T32(x9 + xj); \ - x9 = ROTL32(x9, 11); \ - xg = T32(xa + xg); \ - xa = ROTL32(xa, 11); \ - xh = T32(xb + xh); \ - xb = ROTL32(xb, 11); \ - xm = T32(xc + xm); \ - xc = ROTL32(xc, 11); \ - xn = T32(xd + xn); \ - xd = ROTL32(xd, 11); \ - xk = T32(xe + xk); \ - xe = ROTL32(xe, 11); \ - xl = T32(xf + xl); \ - xf = ROTL32(xf, 11); \ - xq = T32(x0 + xq); \ - x0 = ROTL32(x0, 11); \ - xr = T32(x1 + xr); \ - x1 = ROTL32(x1, 11); \ - xo = T32(x2 + xo); \ - x2 = ROTL32(x2, 11); \ - xp = T32(x3 + xp); \ - x3 = ROTL32(x3, 11); \ - xu = T32(x4 + xu); \ - x4 = ROTL32(x4, 11); \ - xv = T32(x5 + xv); \ - x5 = ROTL32(x5, 11); \ - xs = T32(x6 + xs); \ - x6 = ROTL32(x6, 11); \ - xt = T32(x7 + xt); \ - x7 = ROTL32(x7, 11); \ - xc ^= xi; \ - xd ^= xj; \ - xe ^= xg; \ - xf ^= xh; \ - x8 ^= xm; \ - x9 ^= xn; \ - xa ^= xk; \ - xb ^= xl; \ - x4 ^= xq; \ - x5 ^= xr; \ - x6 ^= xo; \ - x7 ^= xp; \ - x0 ^= xu; \ - x1 ^= xv; \ - x2 ^= xs; \ - x3 ^= xt; \ - } while (0) - -#define ROUND_ODD do { \ - xj = T32(xc + xj); \ - xc = ROTL32(xc, 7); \ - xi = T32(xd + xi); \ - xd = ROTL32(xd, 7); \ - xh = T32(xe + xh); \ - xe = ROTL32(xe, 7); \ - xg = T32(xf + xg); \ - xf = ROTL32(xf, 7); \ - xn = T32(x8 + xn); \ - x8 = ROTL32(x8, 7); \ - xm = T32(x9 + xm); \ - x9 = ROTL32(x9, 7); \ - xl = T32(xa + xl); \ - xa = ROTL32(xa, 7); \ - xk = T32(xb + xk); \ - xb = ROTL32(xb, 7); \ - xr = T32(x4 + xr); \ - x4 = ROTL32(x4, 7); \ - xq = T32(x5 + xq); \ - x5 = ROTL32(x5, 7); \ - xp = T32(x6 + xp); \ - x6 = ROTL32(x6, 7); \ - xo = T32(x7 + xo); \ - x7 = ROTL32(x7, 7); \ - xv = T32(x0 + xv); \ - x0 = ROTL32(x0, 7); \ - xu = T32(x1 + xu); \ - x1 = ROTL32(x1, 7); \ - xt = T32(x2 + xt); \ - x2 = ROTL32(x2, 7); \ - xs = T32(x3 + xs); \ - x3 = ROTL32(x3, 7); \ - x4 ^= xj; \ - x5 ^= xi; \ - x6 ^= xh; \ - x7 ^= xg; \ - x0 ^= xn; \ - x1 ^= xm; \ - x2 ^= xl; \ - x3 ^= xk; \ - xc ^= xr; \ - xd ^= xq; \ - xe ^= xp; \ - xf ^= xo; \ - x8 ^= xv; \ - x9 ^= xu; \ - xa ^= xt; \ - xb ^= xs; \ - xh = T32(x4 + xh); \ - x4 = ROTL32(x4, 11); \ - xg = T32(x5 + xg); \ - x5 = ROTL32(x5, 11); \ - xj = T32(x6 + xj); \ - x6 = ROTL32(x6, 11); \ - xi = T32(x7 + xi); \ - x7 = ROTL32(x7, 11); \ - xl = T32(x0 + xl); \ - x0 = ROTL32(x0, 11); \ - xk = T32(x1 + xk); \ - x1 = ROTL32(x1, 11); \ - xn = T32(x2 + xn); \ - x2 = ROTL32(x2, 11); \ - xm = T32(x3 + xm); \ - x3 = ROTL32(x3, 11); \ - xp = T32(xc + xp); \ - xc = ROTL32(xc, 11); \ - xo = T32(xd + xo); \ - xd = ROTL32(xd, 11); \ - xr = T32(xe + xr); \ - xe = ROTL32(xe, 11); \ - xq = T32(xf + xq); \ - xf = ROTL32(xf, 11); \ - xt = T32(x8 + xt); \ - x8 = ROTL32(x8, 11); \ - xs = T32(x9 + xs); \ - x9 = ROTL32(x9, 11); \ - xv = T32(xa + xv); \ - xa = ROTL32(xa, 11); \ - xu = T32(xb + xu); \ - xb = ROTL32(xb, 11); \ - x0 ^= xh; \ - x1 ^= xg; \ - x2 ^= xj; \ - x3 ^= xi; \ - x4 ^= xl; \ - x5 ^= xk; \ - x6 ^= xn; \ - x7 ^= xm; \ - x8 ^= xp; \ - x9 ^= xo; \ - xa ^= xr; \ - xb ^= xq; \ - xc ^= xt; \ - xd ^= xs; \ - xe ^= xv; \ - xf ^= xu; \ - } while (0) - -/* - * There is no need to unroll all 16 rounds. The word-swapping permutation - * is an involution, so we need to unroll an even number of rounds. On - * "big" systems, unrolling 4 rounds yields about 97% of the speed - * achieved with full unrolling; and it keeps the code more compact - * for small architectures. - */ - -#if SPH_CUBEHASH_UNROLL == 2 - -#define SIXTEEN_ROUNDS do { \ - int j; \ - for (j = 0; j < 8; j ++) { \ - ROUND_EVEN; \ - ROUND_ODD; \ - } \ - } while (0) - -#elif SPH_CUBEHASH_UNROLL == 4 - -#define SIXTEEN_ROUNDS do { \ - int j; \ - for (j = 0; j < 4; j ++) { \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - } \ - } while (0) - -#elif SPH_CUBEHASH_UNROLL == 8 - -#define SIXTEEN_ROUNDS do { \ - int j; \ - for (j = 0; j < 2; j ++) { \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - } \ - } while (0) - -#else - -#define SIXTEEN_ROUNDS do { \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - ROUND_EVEN; \ - ROUND_ODD; \ - } while (0) - -#endif - -static void -cubehash_init(sph_cubehash_context *sc, const sph_u32 *iv) -{ - memcpy(sc->state, iv, sizeof sc->state); - sc->ptr = 0; -} - -static void -cubehash_core(sph_cubehash_context *sc, const void *data, size_t len) -{ - unsigned char *buf; - size_t ptr; - DECL_STATE - - buf = sc->buf; - ptr = sc->ptr; - if (len < (sizeof sc->buf) - ptr) { - memcpy(buf + ptr, data, len); - ptr += len; - sc->ptr = ptr; - return; - } - - READ_STATE(sc); - while (len > 0) { - size_t clen; - - clen = (sizeof sc->buf) - ptr; - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - ptr += clen; - data = (const unsigned char *)data + clen; - len -= clen; - if (ptr == sizeof sc->buf) { - INPUT_BLOCK; - SIXTEEN_ROUNDS; - ptr = 0; - } - } - WRITE_STATE(sc); - sc->ptr = ptr; -} - -static void -cubehash_close(sph_cubehash_context *sc, unsigned ub, unsigned n, - void *dst, size_t out_size_w32) -{ - unsigned char *buf, *out; - size_t ptr; - unsigned z; - int i; - DECL_STATE - - buf = sc->buf; - ptr = sc->ptr; - z = 0x80 >> n; - buf[ptr ++] = ((ub & -z) | z) & 0xFF; - memset(buf + ptr, 0, (sizeof sc->buf) - ptr); - READ_STATE(sc); - INPUT_BLOCK; - for (i = 0; i < 11; i ++) { - SIXTEEN_ROUNDS; - if (i == 0) - xv ^= SPH_C32(1); - } - WRITE_STATE(sc); - out = dst; - for (z = 0; z < out_size_w32; z ++) - sph_enc32le(out + (z << 2), sc->state[z]); -} - -/* see sph_cubehash.h */ -void -sph_cubehash224_init(void *cc) -{ - cubehash_init(cc, IV224); -} - -/* see sph_cubehash.h */ -void -sph_cubehash224(void *cc, const void *data, size_t len) -{ - cubehash_core(cc, data, len); -} - -/* see sph_cubehash.h */ -void -sph_cubehash224_close(void *cc, void *dst) -{ - sph_cubehash224_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_cubehash.h */ -void -sph_cubehash224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - cubehash_close(cc, ub, n, dst, 7); - sph_cubehash224_init(cc); -} - -/* see sph_cubehash.h */ -void -sph_cubehash256_init(void *cc) -{ - cubehash_init(cc, IV256); -} - -/* see sph_cubehash.h */ -void -sph_cubehash256(void *cc, const void *data, size_t len) -{ - cubehash_core(cc, data, len); -} - -/* see sph_cubehash.h */ -void -sph_cubehash256_close(void *cc, void *dst) -{ - sph_cubehash256_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_cubehash.h */ -void -sph_cubehash256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - cubehash_close(cc, ub, n, dst, 8); - sph_cubehash256_init(cc); -} - -/* see sph_cubehash.h */ -void -sph_cubehash384_init(void *cc) -{ - cubehash_init(cc, IV384); -} - -/* see sph_cubehash.h */ -void -sph_cubehash384(void *cc, const void *data, size_t len) -{ - cubehash_core(cc, data, len); -} - -/* see sph_cubehash.h */ -void -sph_cubehash384_close(void *cc, void *dst) -{ - sph_cubehash384_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_cubehash.h */ -void -sph_cubehash384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - cubehash_close(cc, ub, n, dst, 12); - sph_cubehash384_init(cc); -} - -/* see sph_cubehash.h */ -void -sph_cubehash512_init(void *cc) -{ - cubehash_init(cc, IV512); -} - -/* see sph_cubehash.h */ -void -sph_cubehash512(void *cc, const void *data, size_t len) -{ - cubehash_core(cc, data, len); -} - -/* see sph_cubehash.h */ -void -sph_cubehash512_close(void *cc, void *dst) -{ - sph_cubehash512_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_cubehash.h */ -void -sph_cubehash512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - cubehash_close(cc, ub, n, dst, 16); - sph_cubehash512_init(cc); -} -#ifdef __cplusplus -} -#endif diff --git a/src/crypto/groestl.c b/src/crypto/groestl.c deleted file mode 100644 index 928bc41efab9..000000000000 --- a/src/crypto/groestl.c +++ /dev/null @@ -1,3123 +0,0 @@ -/* $Id: groestl.c 260 2011-07-21 01:02:38Z tp $ */ -/* - * Groestl implementation. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @author Thomas Pornin - */ - -#include -#include - -#include "sph_groestl.h" - -#ifdef __cplusplus -extern "C"{ -#endif - -#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_GROESTL -#define SPH_SMALL_FOOTPRINT_GROESTL 1 -#endif - -/* - * Apparently, the 32-bit-only version is not faster than the 64-bit - * version unless using the "small footprint" code on a 32-bit machine. - */ -#if !defined SPH_GROESTL_64 -#if SPH_SMALL_FOOTPRINT_GROESTL && !SPH_64_TRUE -#define SPH_GROESTL_64 0 -#else -#define SPH_GROESTL_64 1 -#endif -#endif - -#if !SPH_64 -#undef SPH_GROESTL_64 -#endif - -#ifdef _MSC_VER -#pragma warning (disable: 4146) -#endif - -/* - * The internal representation may use either big-endian or - * little-endian. Using the platform default representation speeds up - * encoding and decoding between bytes and the matrix columns. - */ - -#undef USE_LE -#if SPH_GROESTL_LITTLE_ENDIAN -#define USE_LE 1 -#elif SPH_GROESTL_BIG_ENDIAN -#define USE_LE 0 -#elif SPH_LITTLE_ENDIAN -#define USE_LE 1 -#endif - -#if USE_LE - -#define C32e(x) ((SPH_C32(x) >> 24) \ - | ((SPH_C32(x) >> 8) & SPH_C32(0x0000FF00)) \ - | ((SPH_C32(x) << 8) & SPH_C32(0x00FF0000)) \ - | ((SPH_C32(x) << 24) & SPH_C32(0xFF000000))) -#define dec32e_aligned sph_dec32le_aligned -#define enc32e sph_enc32le -#define B32_0(x) ((x) & 0xFF) -#define B32_1(x) (((x) >> 8) & 0xFF) -#define B32_2(x) (((x) >> 16) & 0xFF) -#define B32_3(x) ((x) >> 24) - -#define R32u(u, d) SPH_T32(((u) << 16) | ((d) >> 16)) -#define R32d(u, d) SPH_T32(((u) >> 16) | ((d) << 16)) - -#define PC32up(j, r) ((sph_u32)((j) + (r))) -#define PC32dn(j, r) 0 -#define QC32up(j, r) SPH_C32(0xFFFFFFFF) -#define QC32dn(j, r) (((sph_u32)(r) << 24) ^ SPH_T32(~((sph_u32)(j) << 24))) - -#if SPH_64 -#define C64e(x) ((SPH_C64(x) >> 56) \ - | ((SPH_C64(x) >> 40) & SPH_C64(0x000000000000FF00)) \ - | ((SPH_C64(x) >> 24) & SPH_C64(0x0000000000FF0000)) \ - | ((SPH_C64(x) >> 8) & SPH_C64(0x00000000FF000000)) \ - | ((SPH_C64(x) << 8) & SPH_C64(0x000000FF00000000)) \ - | ((SPH_C64(x) << 24) & SPH_C64(0x0000FF0000000000)) \ - | ((SPH_C64(x) << 40) & SPH_C64(0x00FF000000000000)) \ - | ((SPH_C64(x) << 56) & SPH_C64(0xFF00000000000000))) -#define dec64e_aligned sph_dec64le_aligned -#define enc64e sph_enc64le -#define B64_0(x) ((x) & 0xFF) -#define B64_1(x) (((x) >> 8) & 0xFF) -#define B64_2(x) (((x) >> 16) & 0xFF) -#define B64_3(x) (((x) >> 24) & 0xFF) -#define B64_4(x) (((x) >> 32) & 0xFF) -#define B64_5(x) (((x) >> 40) & 0xFF) -#define B64_6(x) (((x) >> 48) & 0xFF) -#define B64_7(x) ((x) >> 56) -#define R64 SPH_ROTL64 -#define PC64(j, r) ((sph_u64)((j) + (r))) -#define QC64(j, r) (((sph_u64)(r) << 56) ^ SPH_T64(~((sph_u64)(j) << 56))) -#endif - -#else - -#define C32e(x) SPH_C32(x) -#define dec32e_aligned sph_dec32be_aligned -#define enc32e sph_enc32be -#define B32_0(x) ((x) >> 24) -#define B32_1(x) (((x) >> 16) & 0xFF) -#define B32_2(x) (((x) >> 8) & 0xFF) -#define B32_3(x) ((x) & 0xFF) - -#define R32u(u, d) SPH_T32(((u) >> 16) | ((d) << 16)) -#define R32d(u, d) SPH_T32(((u) << 16) | ((d) >> 16)) - -#define PC32up(j, r) ((sph_u32)((j) + (r)) << 24) -#define PC32dn(j, r) 0 -#define QC32up(j, r) SPH_C32(0xFFFFFFFF) -#define QC32dn(j, r) ((sph_u32)(r) ^ SPH_T32(~(sph_u32)(j))) - -#if SPH_64 -#define C64e(x) SPH_C64(x) -#define dec64e_aligned sph_dec64be_aligned -#define enc64e sph_enc64be -#define B64_0(x) ((x) >> 56) -#define B64_1(x) (((x) >> 48) & 0xFF) -#define B64_2(x) (((x) >> 40) & 0xFF) -#define B64_3(x) (((x) >> 32) & 0xFF) -#define B64_4(x) (((x) >> 24) & 0xFF) -#define B64_5(x) (((x) >> 16) & 0xFF) -#define B64_6(x) (((x) >> 8) & 0xFF) -#define B64_7(x) ((x) & 0xFF) -#define R64 SPH_ROTR64 -#define PC64(j, r) ((sph_u64)((j) + (r)) << 56) -#define QC64(j, r) ((sph_u64)(r) ^ SPH_T64(~(sph_u64)(j))) -#endif - -#endif - -#if SPH_GROESTL_64 - -static const sph_u64 T0[] = { - C64e(0xc632f4a5f497a5c6), C64e(0xf86f978497eb84f8), - C64e(0xee5eb099b0c799ee), C64e(0xf67a8c8d8cf78df6), - C64e(0xffe8170d17e50dff), C64e(0xd60adcbddcb7bdd6), - C64e(0xde16c8b1c8a7b1de), C64e(0x916dfc54fc395491), - C64e(0x6090f050f0c05060), C64e(0x0207050305040302), - C64e(0xce2ee0a9e087a9ce), C64e(0x56d1877d87ac7d56), - C64e(0xe7cc2b192bd519e7), C64e(0xb513a662a67162b5), - C64e(0x4d7c31e6319ae64d), C64e(0xec59b59ab5c39aec), - C64e(0x8f40cf45cf05458f), C64e(0x1fa3bc9dbc3e9d1f), - C64e(0x8949c040c0094089), C64e(0xfa68928792ef87fa), - C64e(0xefd03f153fc515ef), C64e(0xb29426eb267febb2), - C64e(0x8ece40c94007c98e), C64e(0xfbe61d0b1ded0bfb), - C64e(0x416e2fec2f82ec41), C64e(0xb31aa967a97d67b3), - C64e(0x5f431cfd1cbefd5f), C64e(0x456025ea258aea45), - C64e(0x23f9dabfda46bf23), C64e(0x535102f702a6f753), - C64e(0xe445a196a1d396e4), C64e(0x9b76ed5bed2d5b9b), - C64e(0x75285dc25deac275), C64e(0xe1c5241c24d91ce1), - C64e(0x3dd4e9aee97aae3d), C64e(0x4cf2be6abe986a4c), - C64e(0x6c82ee5aeed85a6c), C64e(0x7ebdc341c3fc417e), - C64e(0xf5f3060206f102f5), C64e(0x8352d14fd11d4f83), - C64e(0x688ce45ce4d05c68), C64e(0x515607f407a2f451), - C64e(0xd18d5c345cb934d1), C64e(0xf9e1180818e908f9), - C64e(0xe24cae93aedf93e2), C64e(0xab3e9573954d73ab), - C64e(0x6297f553f5c45362), C64e(0x2a6b413f41543f2a), - C64e(0x081c140c14100c08), C64e(0x9563f652f6315295), - C64e(0x46e9af65af8c6546), C64e(0x9d7fe25ee2215e9d), - C64e(0x3048782878602830), C64e(0x37cff8a1f86ea137), - C64e(0x0a1b110f11140f0a), C64e(0x2febc4b5c45eb52f), - C64e(0x0e151b091b1c090e), C64e(0x247e5a365a483624), - C64e(0x1badb69bb6369b1b), C64e(0xdf98473d47a53ddf), - C64e(0xcda76a266a8126cd), C64e(0x4ef5bb69bb9c694e), - C64e(0x7f334ccd4cfecd7f), C64e(0xea50ba9fbacf9fea), - C64e(0x123f2d1b2d241b12), C64e(0x1da4b99eb93a9e1d), - C64e(0x58c49c749cb07458), C64e(0x3446722e72682e34), - C64e(0x3641772d776c2d36), C64e(0xdc11cdb2cda3b2dc), - C64e(0xb49d29ee2973eeb4), C64e(0x5b4d16fb16b6fb5b), - C64e(0xa4a501f60153f6a4), C64e(0x76a1d74dd7ec4d76), - C64e(0xb714a361a37561b7), C64e(0x7d3449ce49face7d), - C64e(0x52df8d7b8da47b52), C64e(0xdd9f423e42a13edd), - C64e(0x5ecd937193bc715e), C64e(0x13b1a297a2269713), - C64e(0xa6a204f50457f5a6), C64e(0xb901b868b86968b9), - C64e(0x0000000000000000), C64e(0xc1b5742c74992cc1), - C64e(0x40e0a060a0806040), C64e(0xe3c2211f21dd1fe3), - C64e(0x793a43c843f2c879), C64e(0xb69a2ced2c77edb6), - C64e(0xd40dd9bed9b3bed4), C64e(0x8d47ca46ca01468d), - C64e(0x671770d970ced967), C64e(0x72afdd4bdde44b72), - C64e(0x94ed79de7933de94), C64e(0x98ff67d4672bd498), - C64e(0xb09323e8237be8b0), C64e(0x855bde4ade114a85), - C64e(0xbb06bd6bbd6d6bbb), C64e(0xc5bb7e2a7e912ac5), - C64e(0x4f7b34e5349ee54f), C64e(0xedd73a163ac116ed), - C64e(0x86d254c55417c586), C64e(0x9af862d7622fd79a), - C64e(0x6699ff55ffcc5566), C64e(0x11b6a794a7229411), - C64e(0x8ac04acf4a0fcf8a), C64e(0xe9d9301030c910e9), - C64e(0x040e0a060a080604), C64e(0xfe66988198e781fe), - C64e(0xa0ab0bf00b5bf0a0), C64e(0x78b4cc44ccf04478), - C64e(0x25f0d5bad54aba25), C64e(0x4b753ee33e96e34b), - C64e(0xa2ac0ef30e5ff3a2), C64e(0x5d4419fe19bafe5d), - C64e(0x80db5bc05b1bc080), C64e(0x0580858a850a8a05), - C64e(0x3fd3ecadec7ead3f), C64e(0x21fedfbcdf42bc21), - C64e(0x70a8d848d8e04870), C64e(0xf1fd0c040cf904f1), - C64e(0x63197adf7ac6df63), C64e(0x772f58c158eec177), - C64e(0xaf309f759f4575af), C64e(0x42e7a563a5846342), - C64e(0x2070503050403020), C64e(0xe5cb2e1a2ed11ae5), - C64e(0xfdef120e12e10efd), C64e(0xbf08b76db7656dbf), - C64e(0x8155d44cd4194c81), C64e(0x18243c143c301418), - C64e(0x26795f355f4c3526), C64e(0xc3b2712f719d2fc3), - C64e(0xbe8638e13867e1be), C64e(0x35c8fda2fd6aa235), - C64e(0x88c74fcc4f0bcc88), C64e(0x2e654b394b5c392e), - C64e(0x936af957f93d5793), C64e(0x55580df20daaf255), - C64e(0xfc619d829de382fc), C64e(0x7ab3c947c9f4477a), - C64e(0xc827efacef8bacc8), C64e(0xba8832e7326fe7ba), - C64e(0x324f7d2b7d642b32), C64e(0xe642a495a4d795e6), - C64e(0xc03bfba0fb9ba0c0), C64e(0x19aab398b3329819), - C64e(0x9ef668d16827d19e), C64e(0xa322817f815d7fa3), - C64e(0x44eeaa66aa886644), C64e(0x54d6827e82a87e54), - C64e(0x3bdde6abe676ab3b), C64e(0x0b959e839e16830b), - C64e(0x8cc945ca4503ca8c), C64e(0xc7bc7b297b9529c7), - C64e(0x6b056ed36ed6d36b), C64e(0x286c443c44503c28), - C64e(0xa72c8b798b5579a7), C64e(0xbc813de23d63e2bc), - C64e(0x1631271d272c1d16), C64e(0xad379a769a4176ad), - C64e(0xdb964d3b4dad3bdb), C64e(0x649efa56fac85664), - C64e(0x74a6d24ed2e84e74), C64e(0x1436221e22281e14), - C64e(0x92e476db763fdb92), C64e(0x0c121e0a1e180a0c), - C64e(0x48fcb46cb4906c48), C64e(0xb88f37e4376be4b8), - C64e(0x9f78e75de7255d9f), C64e(0xbd0fb26eb2616ebd), - C64e(0x43692aef2a86ef43), C64e(0xc435f1a6f193a6c4), - C64e(0x39dae3a8e372a839), C64e(0x31c6f7a4f762a431), - C64e(0xd38a593759bd37d3), C64e(0xf274868b86ff8bf2), - C64e(0xd583563256b132d5), C64e(0x8b4ec543c50d438b), - C64e(0x6e85eb59ebdc596e), C64e(0xda18c2b7c2afb7da), - C64e(0x018e8f8c8f028c01), C64e(0xb11dac64ac7964b1), - C64e(0x9cf16dd26d23d29c), C64e(0x49723be03b92e049), - C64e(0xd81fc7b4c7abb4d8), C64e(0xacb915fa1543faac), - C64e(0xf3fa090709fd07f3), C64e(0xcfa06f256f8525cf), - C64e(0xca20eaafea8fafca), C64e(0xf47d898e89f38ef4), - C64e(0x476720e9208ee947), C64e(0x1038281828201810), - C64e(0x6f0b64d564ded56f), C64e(0xf073838883fb88f0), - C64e(0x4afbb16fb1946f4a), C64e(0x5cca967296b8725c), - C64e(0x38546c246c702438), C64e(0x575f08f108aef157), - C64e(0x732152c752e6c773), C64e(0x9764f351f3355197), - C64e(0xcbae6523658d23cb), C64e(0xa125847c84597ca1), - C64e(0xe857bf9cbfcb9ce8), C64e(0x3e5d6321637c213e), - C64e(0x96ea7cdd7c37dd96), C64e(0x611e7fdc7fc2dc61), - C64e(0x0d9c9186911a860d), C64e(0x0f9b9485941e850f), - C64e(0xe04bab90abdb90e0), C64e(0x7cbac642c6f8427c), - C64e(0x712657c457e2c471), C64e(0xcc29e5aae583aacc), - C64e(0x90e373d8733bd890), C64e(0x06090f050f0c0506), - C64e(0xf7f4030103f501f7), C64e(0x1c2a36123638121c), - C64e(0xc23cfea3fe9fa3c2), C64e(0x6a8be15fe1d45f6a), - C64e(0xaebe10f91047f9ae), C64e(0x69026bd06bd2d069), - C64e(0x17bfa891a82e9117), C64e(0x9971e858e8295899), - C64e(0x3a5369276974273a), C64e(0x27f7d0b9d04eb927), - C64e(0xd991483848a938d9), C64e(0xebde351335cd13eb), - C64e(0x2be5ceb3ce56b32b), C64e(0x2277553355443322), - C64e(0xd204d6bbd6bfbbd2), C64e(0xa9399070904970a9), - C64e(0x07878089800e8907), C64e(0x33c1f2a7f266a733), - C64e(0x2decc1b6c15ab62d), C64e(0x3c5a66226678223c), - C64e(0x15b8ad92ad2a9215), C64e(0xc9a96020608920c9), - C64e(0x875cdb49db154987), C64e(0xaab01aff1a4fffaa), - C64e(0x50d8887888a07850), C64e(0xa52b8e7a8e517aa5), - C64e(0x03898a8f8a068f03), C64e(0x594a13f813b2f859), - C64e(0x09929b809b128009), C64e(0x1a2339173934171a), - C64e(0x651075da75cada65), C64e(0xd784533153b531d7), - C64e(0x84d551c65113c684), C64e(0xd003d3b8d3bbb8d0), - C64e(0x82dc5ec35e1fc382), C64e(0x29e2cbb0cb52b029), - C64e(0x5ac3997799b4775a), C64e(0x1e2d3311333c111e), - C64e(0x7b3d46cb46f6cb7b), C64e(0xa8b71ffc1f4bfca8), - C64e(0x6d0c61d661dad66d), C64e(0x2c624e3a4e583a2c) -}; - -#if !SPH_SMALL_FOOTPRINT_GROESTL - -static const sph_u64 T1[] = { - C64e(0xc6c632f4a5f497a5), C64e(0xf8f86f978497eb84), - C64e(0xeeee5eb099b0c799), C64e(0xf6f67a8c8d8cf78d), - C64e(0xffffe8170d17e50d), C64e(0xd6d60adcbddcb7bd), - C64e(0xdede16c8b1c8a7b1), C64e(0x91916dfc54fc3954), - C64e(0x606090f050f0c050), C64e(0x0202070503050403), - C64e(0xcece2ee0a9e087a9), C64e(0x5656d1877d87ac7d), - C64e(0xe7e7cc2b192bd519), C64e(0xb5b513a662a67162), - C64e(0x4d4d7c31e6319ae6), C64e(0xecec59b59ab5c39a), - C64e(0x8f8f40cf45cf0545), C64e(0x1f1fa3bc9dbc3e9d), - C64e(0x898949c040c00940), C64e(0xfafa68928792ef87), - C64e(0xefefd03f153fc515), C64e(0xb2b29426eb267feb), - C64e(0x8e8ece40c94007c9), C64e(0xfbfbe61d0b1ded0b), - C64e(0x41416e2fec2f82ec), C64e(0xb3b31aa967a97d67), - C64e(0x5f5f431cfd1cbefd), C64e(0x45456025ea258aea), - C64e(0x2323f9dabfda46bf), C64e(0x53535102f702a6f7), - C64e(0xe4e445a196a1d396), C64e(0x9b9b76ed5bed2d5b), - C64e(0x7575285dc25deac2), C64e(0xe1e1c5241c24d91c), - C64e(0x3d3dd4e9aee97aae), C64e(0x4c4cf2be6abe986a), - C64e(0x6c6c82ee5aeed85a), C64e(0x7e7ebdc341c3fc41), - C64e(0xf5f5f3060206f102), C64e(0x838352d14fd11d4f), - C64e(0x68688ce45ce4d05c), C64e(0x51515607f407a2f4), - C64e(0xd1d18d5c345cb934), C64e(0xf9f9e1180818e908), - C64e(0xe2e24cae93aedf93), C64e(0xabab3e9573954d73), - C64e(0x626297f553f5c453), C64e(0x2a2a6b413f41543f), - C64e(0x08081c140c14100c), C64e(0x959563f652f63152), - C64e(0x4646e9af65af8c65), C64e(0x9d9d7fe25ee2215e), - C64e(0x3030487828786028), C64e(0x3737cff8a1f86ea1), - C64e(0x0a0a1b110f11140f), C64e(0x2f2febc4b5c45eb5), - C64e(0x0e0e151b091b1c09), C64e(0x24247e5a365a4836), - C64e(0x1b1badb69bb6369b), C64e(0xdfdf98473d47a53d), - C64e(0xcdcda76a266a8126), C64e(0x4e4ef5bb69bb9c69), - C64e(0x7f7f334ccd4cfecd), C64e(0xeaea50ba9fbacf9f), - C64e(0x12123f2d1b2d241b), C64e(0x1d1da4b99eb93a9e), - C64e(0x5858c49c749cb074), C64e(0x343446722e72682e), - C64e(0x363641772d776c2d), C64e(0xdcdc11cdb2cda3b2), - C64e(0xb4b49d29ee2973ee), C64e(0x5b5b4d16fb16b6fb), - C64e(0xa4a4a501f60153f6), C64e(0x7676a1d74dd7ec4d), - C64e(0xb7b714a361a37561), C64e(0x7d7d3449ce49face), - C64e(0x5252df8d7b8da47b), C64e(0xdddd9f423e42a13e), - C64e(0x5e5ecd937193bc71), C64e(0x1313b1a297a22697), - C64e(0xa6a6a204f50457f5), C64e(0xb9b901b868b86968), - C64e(0x0000000000000000), C64e(0xc1c1b5742c74992c), - C64e(0x4040e0a060a08060), C64e(0xe3e3c2211f21dd1f), - C64e(0x79793a43c843f2c8), C64e(0xb6b69a2ced2c77ed), - C64e(0xd4d40dd9bed9b3be), C64e(0x8d8d47ca46ca0146), - C64e(0x67671770d970ced9), C64e(0x7272afdd4bdde44b), - C64e(0x9494ed79de7933de), C64e(0x9898ff67d4672bd4), - C64e(0xb0b09323e8237be8), C64e(0x85855bde4ade114a), - C64e(0xbbbb06bd6bbd6d6b), C64e(0xc5c5bb7e2a7e912a), - C64e(0x4f4f7b34e5349ee5), C64e(0xededd73a163ac116), - C64e(0x8686d254c55417c5), C64e(0x9a9af862d7622fd7), - C64e(0x666699ff55ffcc55), C64e(0x1111b6a794a72294), - C64e(0x8a8ac04acf4a0fcf), C64e(0xe9e9d9301030c910), - C64e(0x04040e0a060a0806), C64e(0xfefe66988198e781), - C64e(0xa0a0ab0bf00b5bf0), C64e(0x7878b4cc44ccf044), - C64e(0x2525f0d5bad54aba), C64e(0x4b4b753ee33e96e3), - C64e(0xa2a2ac0ef30e5ff3), C64e(0x5d5d4419fe19bafe), - C64e(0x8080db5bc05b1bc0), C64e(0x050580858a850a8a), - C64e(0x3f3fd3ecadec7ead), C64e(0x2121fedfbcdf42bc), - C64e(0x7070a8d848d8e048), C64e(0xf1f1fd0c040cf904), - C64e(0x6363197adf7ac6df), C64e(0x77772f58c158eec1), - C64e(0xafaf309f759f4575), C64e(0x4242e7a563a58463), - C64e(0x2020705030504030), C64e(0xe5e5cb2e1a2ed11a), - C64e(0xfdfdef120e12e10e), C64e(0xbfbf08b76db7656d), - C64e(0x818155d44cd4194c), C64e(0x1818243c143c3014), - C64e(0x2626795f355f4c35), C64e(0xc3c3b2712f719d2f), - C64e(0xbebe8638e13867e1), C64e(0x3535c8fda2fd6aa2), - C64e(0x8888c74fcc4f0bcc), C64e(0x2e2e654b394b5c39), - C64e(0x93936af957f93d57), C64e(0x5555580df20daaf2), - C64e(0xfcfc619d829de382), C64e(0x7a7ab3c947c9f447), - C64e(0xc8c827efacef8bac), C64e(0xbaba8832e7326fe7), - C64e(0x32324f7d2b7d642b), C64e(0xe6e642a495a4d795), - C64e(0xc0c03bfba0fb9ba0), C64e(0x1919aab398b33298), - C64e(0x9e9ef668d16827d1), C64e(0xa3a322817f815d7f), - C64e(0x4444eeaa66aa8866), C64e(0x5454d6827e82a87e), - C64e(0x3b3bdde6abe676ab), C64e(0x0b0b959e839e1683), - C64e(0x8c8cc945ca4503ca), C64e(0xc7c7bc7b297b9529), - C64e(0x6b6b056ed36ed6d3), C64e(0x28286c443c44503c), - C64e(0xa7a72c8b798b5579), C64e(0xbcbc813de23d63e2), - C64e(0x161631271d272c1d), C64e(0xadad379a769a4176), - C64e(0xdbdb964d3b4dad3b), C64e(0x64649efa56fac856), - C64e(0x7474a6d24ed2e84e), C64e(0x141436221e22281e), - C64e(0x9292e476db763fdb), C64e(0x0c0c121e0a1e180a), - C64e(0x4848fcb46cb4906c), C64e(0xb8b88f37e4376be4), - C64e(0x9f9f78e75de7255d), C64e(0xbdbd0fb26eb2616e), - C64e(0x4343692aef2a86ef), C64e(0xc4c435f1a6f193a6), - C64e(0x3939dae3a8e372a8), C64e(0x3131c6f7a4f762a4), - C64e(0xd3d38a593759bd37), C64e(0xf2f274868b86ff8b), - C64e(0xd5d583563256b132), C64e(0x8b8b4ec543c50d43), - C64e(0x6e6e85eb59ebdc59), C64e(0xdada18c2b7c2afb7), - C64e(0x01018e8f8c8f028c), C64e(0xb1b11dac64ac7964), - C64e(0x9c9cf16dd26d23d2), C64e(0x4949723be03b92e0), - C64e(0xd8d81fc7b4c7abb4), C64e(0xacacb915fa1543fa), - C64e(0xf3f3fa090709fd07), C64e(0xcfcfa06f256f8525), - C64e(0xcaca20eaafea8faf), C64e(0xf4f47d898e89f38e), - C64e(0x47476720e9208ee9), C64e(0x1010382818282018), - C64e(0x6f6f0b64d564ded5), C64e(0xf0f073838883fb88), - C64e(0x4a4afbb16fb1946f), C64e(0x5c5cca967296b872), - C64e(0x3838546c246c7024), C64e(0x57575f08f108aef1), - C64e(0x73732152c752e6c7), C64e(0x979764f351f33551), - C64e(0xcbcbae6523658d23), C64e(0xa1a125847c84597c), - C64e(0xe8e857bf9cbfcb9c), C64e(0x3e3e5d6321637c21), - C64e(0x9696ea7cdd7c37dd), C64e(0x61611e7fdc7fc2dc), - C64e(0x0d0d9c9186911a86), C64e(0x0f0f9b9485941e85), - C64e(0xe0e04bab90abdb90), C64e(0x7c7cbac642c6f842), - C64e(0x71712657c457e2c4), C64e(0xcccc29e5aae583aa), - C64e(0x9090e373d8733bd8), C64e(0x0606090f050f0c05), - C64e(0xf7f7f4030103f501), C64e(0x1c1c2a3612363812), - C64e(0xc2c23cfea3fe9fa3), C64e(0x6a6a8be15fe1d45f), - C64e(0xaeaebe10f91047f9), C64e(0x6969026bd06bd2d0), - C64e(0x1717bfa891a82e91), C64e(0x999971e858e82958), - C64e(0x3a3a536927697427), C64e(0x2727f7d0b9d04eb9), - C64e(0xd9d991483848a938), C64e(0xebebde351335cd13), - C64e(0x2b2be5ceb3ce56b3), C64e(0x2222775533554433), - C64e(0xd2d204d6bbd6bfbb), C64e(0xa9a9399070904970), - C64e(0x0707878089800e89), C64e(0x3333c1f2a7f266a7), - C64e(0x2d2decc1b6c15ab6), C64e(0x3c3c5a6622667822), - C64e(0x1515b8ad92ad2a92), C64e(0xc9c9a96020608920), - C64e(0x87875cdb49db1549), C64e(0xaaaab01aff1a4fff), - C64e(0x5050d8887888a078), C64e(0xa5a52b8e7a8e517a), - C64e(0x0303898a8f8a068f), C64e(0x59594a13f813b2f8), - C64e(0x0909929b809b1280), C64e(0x1a1a233917393417), - C64e(0x65651075da75cada), C64e(0xd7d784533153b531), - C64e(0x8484d551c65113c6), C64e(0xd0d003d3b8d3bbb8), - C64e(0x8282dc5ec35e1fc3), C64e(0x2929e2cbb0cb52b0), - C64e(0x5a5ac3997799b477), C64e(0x1e1e2d3311333c11), - C64e(0x7b7b3d46cb46f6cb), C64e(0xa8a8b71ffc1f4bfc), - C64e(0x6d6d0c61d661dad6), C64e(0x2c2c624e3a4e583a) -}; - -static const sph_u64 T2[] = { - C64e(0xa5c6c632f4a5f497), C64e(0x84f8f86f978497eb), - C64e(0x99eeee5eb099b0c7), C64e(0x8df6f67a8c8d8cf7), - C64e(0x0dffffe8170d17e5), C64e(0xbdd6d60adcbddcb7), - C64e(0xb1dede16c8b1c8a7), C64e(0x5491916dfc54fc39), - C64e(0x50606090f050f0c0), C64e(0x0302020705030504), - C64e(0xa9cece2ee0a9e087), C64e(0x7d5656d1877d87ac), - C64e(0x19e7e7cc2b192bd5), C64e(0x62b5b513a662a671), - C64e(0xe64d4d7c31e6319a), C64e(0x9aecec59b59ab5c3), - C64e(0x458f8f40cf45cf05), C64e(0x9d1f1fa3bc9dbc3e), - C64e(0x40898949c040c009), C64e(0x87fafa68928792ef), - C64e(0x15efefd03f153fc5), C64e(0xebb2b29426eb267f), - C64e(0xc98e8ece40c94007), C64e(0x0bfbfbe61d0b1ded), - C64e(0xec41416e2fec2f82), C64e(0x67b3b31aa967a97d), - C64e(0xfd5f5f431cfd1cbe), C64e(0xea45456025ea258a), - C64e(0xbf2323f9dabfda46), C64e(0xf753535102f702a6), - C64e(0x96e4e445a196a1d3), C64e(0x5b9b9b76ed5bed2d), - C64e(0xc27575285dc25dea), C64e(0x1ce1e1c5241c24d9), - C64e(0xae3d3dd4e9aee97a), C64e(0x6a4c4cf2be6abe98), - C64e(0x5a6c6c82ee5aeed8), C64e(0x417e7ebdc341c3fc), - C64e(0x02f5f5f3060206f1), C64e(0x4f838352d14fd11d), - C64e(0x5c68688ce45ce4d0), C64e(0xf451515607f407a2), - C64e(0x34d1d18d5c345cb9), C64e(0x08f9f9e1180818e9), - C64e(0x93e2e24cae93aedf), C64e(0x73abab3e9573954d), - C64e(0x53626297f553f5c4), C64e(0x3f2a2a6b413f4154), - C64e(0x0c08081c140c1410), C64e(0x52959563f652f631), - C64e(0x654646e9af65af8c), C64e(0x5e9d9d7fe25ee221), - C64e(0x2830304878287860), C64e(0xa13737cff8a1f86e), - C64e(0x0f0a0a1b110f1114), C64e(0xb52f2febc4b5c45e), - C64e(0x090e0e151b091b1c), C64e(0x3624247e5a365a48), - C64e(0x9b1b1badb69bb636), C64e(0x3ddfdf98473d47a5), - C64e(0x26cdcda76a266a81), C64e(0x694e4ef5bb69bb9c), - C64e(0xcd7f7f334ccd4cfe), C64e(0x9feaea50ba9fbacf), - C64e(0x1b12123f2d1b2d24), C64e(0x9e1d1da4b99eb93a), - C64e(0x745858c49c749cb0), C64e(0x2e343446722e7268), - C64e(0x2d363641772d776c), C64e(0xb2dcdc11cdb2cda3), - C64e(0xeeb4b49d29ee2973), C64e(0xfb5b5b4d16fb16b6), - C64e(0xf6a4a4a501f60153), C64e(0x4d7676a1d74dd7ec), - C64e(0x61b7b714a361a375), C64e(0xce7d7d3449ce49fa), - C64e(0x7b5252df8d7b8da4), C64e(0x3edddd9f423e42a1), - C64e(0x715e5ecd937193bc), C64e(0x971313b1a297a226), - C64e(0xf5a6a6a204f50457), C64e(0x68b9b901b868b869), - C64e(0x0000000000000000), C64e(0x2cc1c1b5742c7499), - C64e(0x604040e0a060a080), C64e(0x1fe3e3c2211f21dd), - C64e(0xc879793a43c843f2), C64e(0xedb6b69a2ced2c77), - C64e(0xbed4d40dd9bed9b3), C64e(0x468d8d47ca46ca01), - C64e(0xd967671770d970ce), C64e(0x4b7272afdd4bdde4), - C64e(0xde9494ed79de7933), C64e(0xd49898ff67d4672b), - C64e(0xe8b0b09323e8237b), C64e(0x4a85855bde4ade11), - C64e(0x6bbbbb06bd6bbd6d), C64e(0x2ac5c5bb7e2a7e91), - C64e(0xe54f4f7b34e5349e), C64e(0x16ededd73a163ac1), - C64e(0xc58686d254c55417), C64e(0xd79a9af862d7622f), - C64e(0x55666699ff55ffcc), C64e(0x941111b6a794a722), - C64e(0xcf8a8ac04acf4a0f), C64e(0x10e9e9d9301030c9), - C64e(0x0604040e0a060a08), C64e(0x81fefe66988198e7), - C64e(0xf0a0a0ab0bf00b5b), C64e(0x447878b4cc44ccf0), - C64e(0xba2525f0d5bad54a), C64e(0xe34b4b753ee33e96), - C64e(0xf3a2a2ac0ef30e5f), C64e(0xfe5d5d4419fe19ba), - C64e(0xc08080db5bc05b1b), C64e(0x8a050580858a850a), - C64e(0xad3f3fd3ecadec7e), C64e(0xbc2121fedfbcdf42), - C64e(0x487070a8d848d8e0), C64e(0x04f1f1fd0c040cf9), - C64e(0xdf6363197adf7ac6), C64e(0xc177772f58c158ee), - C64e(0x75afaf309f759f45), C64e(0x634242e7a563a584), - C64e(0x3020207050305040), C64e(0x1ae5e5cb2e1a2ed1), - C64e(0x0efdfdef120e12e1), C64e(0x6dbfbf08b76db765), - C64e(0x4c818155d44cd419), C64e(0x141818243c143c30), - C64e(0x352626795f355f4c), C64e(0x2fc3c3b2712f719d), - C64e(0xe1bebe8638e13867), C64e(0xa23535c8fda2fd6a), - C64e(0xcc8888c74fcc4f0b), C64e(0x392e2e654b394b5c), - C64e(0x5793936af957f93d), C64e(0xf25555580df20daa), - C64e(0x82fcfc619d829de3), C64e(0x477a7ab3c947c9f4), - C64e(0xacc8c827efacef8b), C64e(0xe7baba8832e7326f), - C64e(0x2b32324f7d2b7d64), C64e(0x95e6e642a495a4d7), - C64e(0xa0c0c03bfba0fb9b), C64e(0x981919aab398b332), - C64e(0xd19e9ef668d16827), C64e(0x7fa3a322817f815d), - C64e(0x664444eeaa66aa88), C64e(0x7e5454d6827e82a8), - C64e(0xab3b3bdde6abe676), C64e(0x830b0b959e839e16), - C64e(0xca8c8cc945ca4503), C64e(0x29c7c7bc7b297b95), - C64e(0xd36b6b056ed36ed6), C64e(0x3c28286c443c4450), - C64e(0x79a7a72c8b798b55), C64e(0xe2bcbc813de23d63), - C64e(0x1d161631271d272c), C64e(0x76adad379a769a41), - C64e(0x3bdbdb964d3b4dad), C64e(0x5664649efa56fac8), - C64e(0x4e7474a6d24ed2e8), C64e(0x1e141436221e2228), - C64e(0xdb9292e476db763f), C64e(0x0a0c0c121e0a1e18), - C64e(0x6c4848fcb46cb490), C64e(0xe4b8b88f37e4376b), - C64e(0x5d9f9f78e75de725), C64e(0x6ebdbd0fb26eb261), - C64e(0xef4343692aef2a86), C64e(0xa6c4c435f1a6f193), - C64e(0xa83939dae3a8e372), C64e(0xa43131c6f7a4f762), - C64e(0x37d3d38a593759bd), C64e(0x8bf2f274868b86ff), - C64e(0x32d5d583563256b1), C64e(0x438b8b4ec543c50d), - C64e(0x596e6e85eb59ebdc), C64e(0xb7dada18c2b7c2af), - C64e(0x8c01018e8f8c8f02), C64e(0x64b1b11dac64ac79), - C64e(0xd29c9cf16dd26d23), C64e(0xe04949723be03b92), - C64e(0xb4d8d81fc7b4c7ab), C64e(0xfaacacb915fa1543), - C64e(0x07f3f3fa090709fd), C64e(0x25cfcfa06f256f85), - C64e(0xafcaca20eaafea8f), C64e(0x8ef4f47d898e89f3), - C64e(0xe947476720e9208e), C64e(0x1810103828182820), - C64e(0xd56f6f0b64d564de), C64e(0x88f0f073838883fb), - C64e(0x6f4a4afbb16fb194), C64e(0x725c5cca967296b8), - C64e(0x243838546c246c70), C64e(0xf157575f08f108ae), - C64e(0xc773732152c752e6), C64e(0x51979764f351f335), - C64e(0x23cbcbae6523658d), C64e(0x7ca1a125847c8459), - C64e(0x9ce8e857bf9cbfcb), C64e(0x213e3e5d6321637c), - C64e(0xdd9696ea7cdd7c37), C64e(0xdc61611e7fdc7fc2), - C64e(0x860d0d9c9186911a), C64e(0x850f0f9b9485941e), - C64e(0x90e0e04bab90abdb), C64e(0x427c7cbac642c6f8), - C64e(0xc471712657c457e2), C64e(0xaacccc29e5aae583), - C64e(0xd89090e373d8733b), C64e(0x050606090f050f0c), - C64e(0x01f7f7f4030103f5), C64e(0x121c1c2a36123638), - C64e(0xa3c2c23cfea3fe9f), C64e(0x5f6a6a8be15fe1d4), - C64e(0xf9aeaebe10f91047), C64e(0xd06969026bd06bd2), - C64e(0x911717bfa891a82e), C64e(0x58999971e858e829), - C64e(0x273a3a5369276974), C64e(0xb92727f7d0b9d04e), - C64e(0x38d9d991483848a9), C64e(0x13ebebde351335cd), - C64e(0xb32b2be5ceb3ce56), C64e(0x3322227755335544), - C64e(0xbbd2d204d6bbd6bf), C64e(0x70a9a93990709049), - C64e(0x890707878089800e), C64e(0xa73333c1f2a7f266), - C64e(0xb62d2decc1b6c15a), C64e(0x223c3c5a66226678), - C64e(0x921515b8ad92ad2a), C64e(0x20c9c9a960206089), - C64e(0x4987875cdb49db15), C64e(0xffaaaab01aff1a4f), - C64e(0x785050d8887888a0), C64e(0x7aa5a52b8e7a8e51), - C64e(0x8f0303898a8f8a06), C64e(0xf859594a13f813b2), - C64e(0x800909929b809b12), C64e(0x171a1a2339173934), - C64e(0xda65651075da75ca), C64e(0x31d7d784533153b5), - C64e(0xc68484d551c65113), C64e(0xb8d0d003d3b8d3bb), - C64e(0xc38282dc5ec35e1f), C64e(0xb02929e2cbb0cb52), - C64e(0x775a5ac3997799b4), C64e(0x111e1e2d3311333c), - C64e(0xcb7b7b3d46cb46f6), C64e(0xfca8a8b71ffc1f4b), - C64e(0xd66d6d0c61d661da), C64e(0x3a2c2c624e3a4e58) -}; - -static const sph_u64 T3[] = { - C64e(0x97a5c6c632f4a5f4), C64e(0xeb84f8f86f978497), - C64e(0xc799eeee5eb099b0), C64e(0xf78df6f67a8c8d8c), - C64e(0xe50dffffe8170d17), C64e(0xb7bdd6d60adcbddc), - C64e(0xa7b1dede16c8b1c8), C64e(0x395491916dfc54fc), - C64e(0xc050606090f050f0), C64e(0x0403020207050305), - C64e(0x87a9cece2ee0a9e0), C64e(0xac7d5656d1877d87), - C64e(0xd519e7e7cc2b192b), C64e(0x7162b5b513a662a6), - C64e(0x9ae64d4d7c31e631), C64e(0xc39aecec59b59ab5), - C64e(0x05458f8f40cf45cf), C64e(0x3e9d1f1fa3bc9dbc), - C64e(0x0940898949c040c0), C64e(0xef87fafa68928792), - C64e(0xc515efefd03f153f), C64e(0x7febb2b29426eb26), - C64e(0x07c98e8ece40c940), C64e(0xed0bfbfbe61d0b1d), - C64e(0x82ec41416e2fec2f), C64e(0x7d67b3b31aa967a9), - C64e(0xbefd5f5f431cfd1c), C64e(0x8aea45456025ea25), - C64e(0x46bf2323f9dabfda), C64e(0xa6f753535102f702), - C64e(0xd396e4e445a196a1), C64e(0x2d5b9b9b76ed5bed), - C64e(0xeac27575285dc25d), C64e(0xd91ce1e1c5241c24), - C64e(0x7aae3d3dd4e9aee9), C64e(0x986a4c4cf2be6abe), - C64e(0xd85a6c6c82ee5aee), C64e(0xfc417e7ebdc341c3), - C64e(0xf102f5f5f3060206), C64e(0x1d4f838352d14fd1), - C64e(0xd05c68688ce45ce4), C64e(0xa2f451515607f407), - C64e(0xb934d1d18d5c345c), C64e(0xe908f9f9e1180818), - C64e(0xdf93e2e24cae93ae), C64e(0x4d73abab3e957395), - C64e(0xc453626297f553f5), C64e(0x543f2a2a6b413f41), - C64e(0x100c08081c140c14), C64e(0x3152959563f652f6), - C64e(0x8c654646e9af65af), C64e(0x215e9d9d7fe25ee2), - C64e(0x6028303048782878), C64e(0x6ea13737cff8a1f8), - C64e(0x140f0a0a1b110f11), C64e(0x5eb52f2febc4b5c4), - C64e(0x1c090e0e151b091b), C64e(0x483624247e5a365a), - C64e(0x369b1b1badb69bb6), C64e(0xa53ddfdf98473d47), - C64e(0x8126cdcda76a266a), C64e(0x9c694e4ef5bb69bb), - C64e(0xfecd7f7f334ccd4c), C64e(0xcf9feaea50ba9fba), - C64e(0x241b12123f2d1b2d), C64e(0x3a9e1d1da4b99eb9), - C64e(0xb0745858c49c749c), C64e(0x682e343446722e72), - C64e(0x6c2d363641772d77), C64e(0xa3b2dcdc11cdb2cd), - C64e(0x73eeb4b49d29ee29), C64e(0xb6fb5b5b4d16fb16), - C64e(0x53f6a4a4a501f601), C64e(0xec4d7676a1d74dd7), - C64e(0x7561b7b714a361a3), C64e(0xface7d7d3449ce49), - C64e(0xa47b5252df8d7b8d), C64e(0xa13edddd9f423e42), - C64e(0xbc715e5ecd937193), C64e(0x26971313b1a297a2), - C64e(0x57f5a6a6a204f504), C64e(0x6968b9b901b868b8), - C64e(0x0000000000000000), C64e(0x992cc1c1b5742c74), - C64e(0x80604040e0a060a0), C64e(0xdd1fe3e3c2211f21), - C64e(0xf2c879793a43c843), C64e(0x77edb6b69a2ced2c), - C64e(0xb3bed4d40dd9bed9), C64e(0x01468d8d47ca46ca), - C64e(0xced967671770d970), C64e(0xe44b7272afdd4bdd), - C64e(0x33de9494ed79de79), C64e(0x2bd49898ff67d467), - C64e(0x7be8b0b09323e823), C64e(0x114a85855bde4ade), - C64e(0x6d6bbbbb06bd6bbd), C64e(0x912ac5c5bb7e2a7e), - C64e(0x9ee54f4f7b34e534), C64e(0xc116ededd73a163a), - C64e(0x17c58686d254c554), C64e(0x2fd79a9af862d762), - C64e(0xcc55666699ff55ff), C64e(0x22941111b6a794a7), - C64e(0x0fcf8a8ac04acf4a), C64e(0xc910e9e9d9301030), - C64e(0x080604040e0a060a), C64e(0xe781fefe66988198), - C64e(0x5bf0a0a0ab0bf00b), C64e(0xf0447878b4cc44cc), - C64e(0x4aba2525f0d5bad5), C64e(0x96e34b4b753ee33e), - C64e(0x5ff3a2a2ac0ef30e), C64e(0xbafe5d5d4419fe19), - C64e(0x1bc08080db5bc05b), C64e(0x0a8a050580858a85), - C64e(0x7ead3f3fd3ecadec), C64e(0x42bc2121fedfbcdf), - C64e(0xe0487070a8d848d8), C64e(0xf904f1f1fd0c040c), - C64e(0xc6df6363197adf7a), C64e(0xeec177772f58c158), - C64e(0x4575afaf309f759f), C64e(0x84634242e7a563a5), - C64e(0x4030202070503050), C64e(0xd11ae5e5cb2e1a2e), - C64e(0xe10efdfdef120e12), C64e(0x656dbfbf08b76db7), - C64e(0x194c818155d44cd4), C64e(0x30141818243c143c), - C64e(0x4c352626795f355f), C64e(0x9d2fc3c3b2712f71), - C64e(0x67e1bebe8638e138), C64e(0x6aa23535c8fda2fd), - C64e(0x0bcc8888c74fcc4f), C64e(0x5c392e2e654b394b), - C64e(0x3d5793936af957f9), C64e(0xaaf25555580df20d), - C64e(0xe382fcfc619d829d), C64e(0xf4477a7ab3c947c9), - C64e(0x8bacc8c827efacef), C64e(0x6fe7baba8832e732), - C64e(0x642b32324f7d2b7d), C64e(0xd795e6e642a495a4), - C64e(0x9ba0c0c03bfba0fb), C64e(0x32981919aab398b3), - C64e(0x27d19e9ef668d168), C64e(0x5d7fa3a322817f81), - C64e(0x88664444eeaa66aa), C64e(0xa87e5454d6827e82), - C64e(0x76ab3b3bdde6abe6), C64e(0x16830b0b959e839e), - C64e(0x03ca8c8cc945ca45), C64e(0x9529c7c7bc7b297b), - C64e(0xd6d36b6b056ed36e), C64e(0x503c28286c443c44), - C64e(0x5579a7a72c8b798b), C64e(0x63e2bcbc813de23d), - C64e(0x2c1d161631271d27), C64e(0x4176adad379a769a), - C64e(0xad3bdbdb964d3b4d), C64e(0xc85664649efa56fa), - C64e(0xe84e7474a6d24ed2), C64e(0x281e141436221e22), - C64e(0x3fdb9292e476db76), C64e(0x180a0c0c121e0a1e), - C64e(0x906c4848fcb46cb4), C64e(0x6be4b8b88f37e437), - C64e(0x255d9f9f78e75de7), C64e(0x616ebdbd0fb26eb2), - C64e(0x86ef4343692aef2a), C64e(0x93a6c4c435f1a6f1), - C64e(0x72a83939dae3a8e3), C64e(0x62a43131c6f7a4f7), - C64e(0xbd37d3d38a593759), C64e(0xff8bf2f274868b86), - C64e(0xb132d5d583563256), C64e(0x0d438b8b4ec543c5), - C64e(0xdc596e6e85eb59eb), C64e(0xafb7dada18c2b7c2), - C64e(0x028c01018e8f8c8f), C64e(0x7964b1b11dac64ac), - C64e(0x23d29c9cf16dd26d), C64e(0x92e04949723be03b), - C64e(0xabb4d8d81fc7b4c7), C64e(0x43faacacb915fa15), - C64e(0xfd07f3f3fa090709), C64e(0x8525cfcfa06f256f), - C64e(0x8fafcaca20eaafea), C64e(0xf38ef4f47d898e89), - C64e(0x8ee947476720e920), C64e(0x2018101038281828), - C64e(0xded56f6f0b64d564), C64e(0xfb88f0f073838883), - C64e(0x946f4a4afbb16fb1), C64e(0xb8725c5cca967296), - C64e(0x70243838546c246c), C64e(0xaef157575f08f108), - C64e(0xe6c773732152c752), C64e(0x3551979764f351f3), - C64e(0x8d23cbcbae652365), C64e(0x597ca1a125847c84), - C64e(0xcb9ce8e857bf9cbf), C64e(0x7c213e3e5d632163), - C64e(0x37dd9696ea7cdd7c), C64e(0xc2dc61611e7fdc7f), - C64e(0x1a860d0d9c918691), C64e(0x1e850f0f9b948594), - C64e(0xdb90e0e04bab90ab), C64e(0xf8427c7cbac642c6), - C64e(0xe2c471712657c457), C64e(0x83aacccc29e5aae5), - C64e(0x3bd89090e373d873), C64e(0x0c050606090f050f), - C64e(0xf501f7f7f4030103), C64e(0x38121c1c2a361236), - C64e(0x9fa3c2c23cfea3fe), C64e(0xd45f6a6a8be15fe1), - C64e(0x47f9aeaebe10f910), C64e(0xd2d06969026bd06b), - C64e(0x2e911717bfa891a8), C64e(0x2958999971e858e8), - C64e(0x74273a3a53692769), C64e(0x4eb92727f7d0b9d0), - C64e(0xa938d9d991483848), C64e(0xcd13ebebde351335), - C64e(0x56b32b2be5ceb3ce), C64e(0x4433222277553355), - C64e(0xbfbbd2d204d6bbd6), C64e(0x4970a9a939907090), - C64e(0x0e89070787808980), C64e(0x66a73333c1f2a7f2), - C64e(0x5ab62d2decc1b6c1), C64e(0x78223c3c5a662266), - C64e(0x2a921515b8ad92ad), C64e(0x8920c9c9a9602060), - C64e(0x154987875cdb49db), C64e(0x4fffaaaab01aff1a), - C64e(0xa0785050d8887888), C64e(0x517aa5a52b8e7a8e), - C64e(0x068f0303898a8f8a), C64e(0xb2f859594a13f813), - C64e(0x12800909929b809b), C64e(0x34171a1a23391739), - C64e(0xcada65651075da75), C64e(0xb531d7d784533153), - C64e(0x13c68484d551c651), C64e(0xbbb8d0d003d3b8d3), - C64e(0x1fc38282dc5ec35e), C64e(0x52b02929e2cbb0cb), - C64e(0xb4775a5ac3997799), C64e(0x3c111e1e2d331133), - C64e(0xf6cb7b7b3d46cb46), C64e(0x4bfca8a8b71ffc1f), - C64e(0xdad66d6d0c61d661), C64e(0x583a2c2c624e3a4e) -}; - -#endif - -static const sph_u64 T4[] = { - C64e(0xf497a5c6c632f4a5), C64e(0x97eb84f8f86f9784), - C64e(0xb0c799eeee5eb099), C64e(0x8cf78df6f67a8c8d), - C64e(0x17e50dffffe8170d), C64e(0xdcb7bdd6d60adcbd), - C64e(0xc8a7b1dede16c8b1), C64e(0xfc395491916dfc54), - C64e(0xf0c050606090f050), C64e(0x0504030202070503), - C64e(0xe087a9cece2ee0a9), C64e(0x87ac7d5656d1877d), - C64e(0x2bd519e7e7cc2b19), C64e(0xa67162b5b513a662), - C64e(0x319ae64d4d7c31e6), C64e(0xb5c39aecec59b59a), - C64e(0xcf05458f8f40cf45), C64e(0xbc3e9d1f1fa3bc9d), - C64e(0xc00940898949c040), C64e(0x92ef87fafa689287), - C64e(0x3fc515efefd03f15), C64e(0x267febb2b29426eb), - C64e(0x4007c98e8ece40c9), C64e(0x1ded0bfbfbe61d0b), - C64e(0x2f82ec41416e2fec), C64e(0xa97d67b3b31aa967), - C64e(0x1cbefd5f5f431cfd), C64e(0x258aea45456025ea), - C64e(0xda46bf2323f9dabf), C64e(0x02a6f753535102f7), - C64e(0xa1d396e4e445a196), C64e(0xed2d5b9b9b76ed5b), - C64e(0x5deac27575285dc2), C64e(0x24d91ce1e1c5241c), - C64e(0xe97aae3d3dd4e9ae), C64e(0xbe986a4c4cf2be6a), - C64e(0xeed85a6c6c82ee5a), C64e(0xc3fc417e7ebdc341), - C64e(0x06f102f5f5f30602), C64e(0xd11d4f838352d14f), - C64e(0xe4d05c68688ce45c), C64e(0x07a2f451515607f4), - C64e(0x5cb934d1d18d5c34), C64e(0x18e908f9f9e11808), - C64e(0xaedf93e2e24cae93), C64e(0x954d73abab3e9573), - C64e(0xf5c453626297f553), C64e(0x41543f2a2a6b413f), - C64e(0x14100c08081c140c), C64e(0xf63152959563f652), - C64e(0xaf8c654646e9af65), C64e(0xe2215e9d9d7fe25e), - C64e(0x7860283030487828), C64e(0xf86ea13737cff8a1), - C64e(0x11140f0a0a1b110f), C64e(0xc45eb52f2febc4b5), - C64e(0x1b1c090e0e151b09), C64e(0x5a483624247e5a36), - C64e(0xb6369b1b1badb69b), C64e(0x47a53ddfdf98473d), - C64e(0x6a8126cdcda76a26), C64e(0xbb9c694e4ef5bb69), - C64e(0x4cfecd7f7f334ccd), C64e(0xbacf9feaea50ba9f), - C64e(0x2d241b12123f2d1b), C64e(0xb93a9e1d1da4b99e), - C64e(0x9cb0745858c49c74), C64e(0x72682e343446722e), - C64e(0x776c2d363641772d), C64e(0xcda3b2dcdc11cdb2), - C64e(0x2973eeb4b49d29ee), C64e(0x16b6fb5b5b4d16fb), - C64e(0x0153f6a4a4a501f6), C64e(0xd7ec4d7676a1d74d), - C64e(0xa37561b7b714a361), C64e(0x49face7d7d3449ce), - C64e(0x8da47b5252df8d7b), C64e(0x42a13edddd9f423e), - C64e(0x93bc715e5ecd9371), C64e(0xa226971313b1a297), - C64e(0x0457f5a6a6a204f5), C64e(0xb86968b9b901b868), - C64e(0x0000000000000000), C64e(0x74992cc1c1b5742c), - C64e(0xa080604040e0a060), C64e(0x21dd1fe3e3c2211f), - C64e(0x43f2c879793a43c8), C64e(0x2c77edb6b69a2ced), - C64e(0xd9b3bed4d40dd9be), C64e(0xca01468d8d47ca46), - C64e(0x70ced967671770d9), C64e(0xdde44b7272afdd4b), - C64e(0x7933de9494ed79de), C64e(0x672bd49898ff67d4), - C64e(0x237be8b0b09323e8), C64e(0xde114a85855bde4a), - C64e(0xbd6d6bbbbb06bd6b), C64e(0x7e912ac5c5bb7e2a), - C64e(0x349ee54f4f7b34e5), C64e(0x3ac116ededd73a16), - C64e(0x5417c58686d254c5), C64e(0x622fd79a9af862d7), - C64e(0xffcc55666699ff55), C64e(0xa722941111b6a794), - C64e(0x4a0fcf8a8ac04acf), C64e(0x30c910e9e9d93010), - C64e(0x0a080604040e0a06), C64e(0x98e781fefe669881), - C64e(0x0b5bf0a0a0ab0bf0), C64e(0xccf0447878b4cc44), - C64e(0xd54aba2525f0d5ba), C64e(0x3e96e34b4b753ee3), - C64e(0x0e5ff3a2a2ac0ef3), C64e(0x19bafe5d5d4419fe), - C64e(0x5b1bc08080db5bc0), C64e(0x850a8a050580858a), - C64e(0xec7ead3f3fd3ecad), C64e(0xdf42bc2121fedfbc), - C64e(0xd8e0487070a8d848), C64e(0x0cf904f1f1fd0c04), - C64e(0x7ac6df6363197adf), C64e(0x58eec177772f58c1), - C64e(0x9f4575afaf309f75), C64e(0xa584634242e7a563), - C64e(0x5040302020705030), C64e(0x2ed11ae5e5cb2e1a), - C64e(0x12e10efdfdef120e), C64e(0xb7656dbfbf08b76d), - C64e(0xd4194c818155d44c), C64e(0x3c30141818243c14), - C64e(0x5f4c352626795f35), C64e(0x719d2fc3c3b2712f), - C64e(0x3867e1bebe8638e1), C64e(0xfd6aa23535c8fda2), - C64e(0x4f0bcc8888c74fcc), C64e(0x4b5c392e2e654b39), - C64e(0xf93d5793936af957), C64e(0x0daaf25555580df2), - C64e(0x9de382fcfc619d82), C64e(0xc9f4477a7ab3c947), - C64e(0xef8bacc8c827efac), C64e(0x326fe7baba8832e7), - C64e(0x7d642b32324f7d2b), C64e(0xa4d795e6e642a495), - C64e(0xfb9ba0c0c03bfba0), C64e(0xb332981919aab398), - C64e(0x6827d19e9ef668d1), C64e(0x815d7fa3a322817f), - C64e(0xaa88664444eeaa66), C64e(0x82a87e5454d6827e), - C64e(0xe676ab3b3bdde6ab), C64e(0x9e16830b0b959e83), - C64e(0x4503ca8c8cc945ca), C64e(0x7b9529c7c7bc7b29), - C64e(0x6ed6d36b6b056ed3), C64e(0x44503c28286c443c), - C64e(0x8b5579a7a72c8b79), C64e(0x3d63e2bcbc813de2), - C64e(0x272c1d161631271d), C64e(0x9a4176adad379a76), - C64e(0x4dad3bdbdb964d3b), C64e(0xfac85664649efa56), - C64e(0xd2e84e7474a6d24e), C64e(0x22281e141436221e), - C64e(0x763fdb9292e476db), C64e(0x1e180a0c0c121e0a), - C64e(0xb4906c4848fcb46c), C64e(0x376be4b8b88f37e4), - C64e(0xe7255d9f9f78e75d), C64e(0xb2616ebdbd0fb26e), - C64e(0x2a86ef4343692aef), C64e(0xf193a6c4c435f1a6), - C64e(0xe372a83939dae3a8), C64e(0xf762a43131c6f7a4), - C64e(0x59bd37d3d38a5937), C64e(0x86ff8bf2f274868b), - C64e(0x56b132d5d5835632), C64e(0xc50d438b8b4ec543), - C64e(0xebdc596e6e85eb59), C64e(0xc2afb7dada18c2b7), - C64e(0x8f028c01018e8f8c), C64e(0xac7964b1b11dac64), - C64e(0x6d23d29c9cf16dd2), C64e(0x3b92e04949723be0), - C64e(0xc7abb4d8d81fc7b4), C64e(0x1543faacacb915fa), - C64e(0x09fd07f3f3fa0907), C64e(0x6f8525cfcfa06f25), - C64e(0xea8fafcaca20eaaf), C64e(0x89f38ef4f47d898e), - C64e(0x208ee947476720e9), C64e(0x2820181010382818), - C64e(0x64ded56f6f0b64d5), C64e(0x83fb88f0f0738388), - C64e(0xb1946f4a4afbb16f), C64e(0x96b8725c5cca9672), - C64e(0x6c70243838546c24), C64e(0x08aef157575f08f1), - C64e(0x52e6c773732152c7), C64e(0xf33551979764f351), - C64e(0x658d23cbcbae6523), C64e(0x84597ca1a125847c), - C64e(0xbfcb9ce8e857bf9c), C64e(0x637c213e3e5d6321), - C64e(0x7c37dd9696ea7cdd), C64e(0x7fc2dc61611e7fdc), - C64e(0x911a860d0d9c9186), C64e(0x941e850f0f9b9485), - C64e(0xabdb90e0e04bab90), C64e(0xc6f8427c7cbac642), - C64e(0x57e2c471712657c4), C64e(0xe583aacccc29e5aa), - C64e(0x733bd89090e373d8), C64e(0x0f0c050606090f05), - C64e(0x03f501f7f7f40301), C64e(0x3638121c1c2a3612), - C64e(0xfe9fa3c2c23cfea3), C64e(0xe1d45f6a6a8be15f), - C64e(0x1047f9aeaebe10f9), C64e(0x6bd2d06969026bd0), - C64e(0xa82e911717bfa891), C64e(0xe82958999971e858), - C64e(0x6974273a3a536927), C64e(0xd04eb92727f7d0b9), - C64e(0x48a938d9d9914838), C64e(0x35cd13ebebde3513), - C64e(0xce56b32b2be5ceb3), C64e(0x5544332222775533), - C64e(0xd6bfbbd2d204d6bb), C64e(0x904970a9a9399070), - C64e(0x800e890707878089), C64e(0xf266a73333c1f2a7), - C64e(0xc15ab62d2decc1b6), C64e(0x6678223c3c5a6622), - C64e(0xad2a921515b8ad92), C64e(0x608920c9c9a96020), - C64e(0xdb154987875cdb49), C64e(0x1a4fffaaaab01aff), - C64e(0x88a0785050d88878), C64e(0x8e517aa5a52b8e7a), - C64e(0x8a068f0303898a8f), C64e(0x13b2f859594a13f8), - C64e(0x9b12800909929b80), C64e(0x3934171a1a233917), - C64e(0x75cada65651075da), C64e(0x53b531d7d7845331), - C64e(0x5113c68484d551c6), C64e(0xd3bbb8d0d003d3b8), - C64e(0x5e1fc38282dc5ec3), C64e(0xcb52b02929e2cbb0), - C64e(0x99b4775a5ac39977), C64e(0x333c111e1e2d3311), - C64e(0x46f6cb7b7b3d46cb), C64e(0x1f4bfca8a8b71ffc), - C64e(0x61dad66d6d0c61d6), C64e(0x4e583a2c2c624e3a) -}; - -#if !SPH_SMALL_FOOTPRINT_GROESTL - -static const sph_u64 T5[] = { - C64e(0xa5f497a5c6c632f4), C64e(0x8497eb84f8f86f97), - C64e(0x99b0c799eeee5eb0), C64e(0x8d8cf78df6f67a8c), - C64e(0x0d17e50dffffe817), C64e(0xbddcb7bdd6d60adc), - C64e(0xb1c8a7b1dede16c8), C64e(0x54fc395491916dfc), - C64e(0x50f0c050606090f0), C64e(0x0305040302020705), - C64e(0xa9e087a9cece2ee0), C64e(0x7d87ac7d5656d187), - C64e(0x192bd519e7e7cc2b), C64e(0x62a67162b5b513a6), - C64e(0xe6319ae64d4d7c31), C64e(0x9ab5c39aecec59b5), - C64e(0x45cf05458f8f40cf), C64e(0x9dbc3e9d1f1fa3bc), - C64e(0x40c00940898949c0), C64e(0x8792ef87fafa6892), - C64e(0x153fc515efefd03f), C64e(0xeb267febb2b29426), - C64e(0xc94007c98e8ece40), C64e(0x0b1ded0bfbfbe61d), - C64e(0xec2f82ec41416e2f), C64e(0x67a97d67b3b31aa9), - C64e(0xfd1cbefd5f5f431c), C64e(0xea258aea45456025), - C64e(0xbfda46bf2323f9da), C64e(0xf702a6f753535102), - C64e(0x96a1d396e4e445a1), C64e(0x5bed2d5b9b9b76ed), - C64e(0xc25deac27575285d), C64e(0x1c24d91ce1e1c524), - C64e(0xaee97aae3d3dd4e9), C64e(0x6abe986a4c4cf2be), - C64e(0x5aeed85a6c6c82ee), C64e(0x41c3fc417e7ebdc3), - C64e(0x0206f102f5f5f306), C64e(0x4fd11d4f838352d1), - C64e(0x5ce4d05c68688ce4), C64e(0xf407a2f451515607), - C64e(0x345cb934d1d18d5c), C64e(0x0818e908f9f9e118), - C64e(0x93aedf93e2e24cae), C64e(0x73954d73abab3e95), - C64e(0x53f5c453626297f5), C64e(0x3f41543f2a2a6b41), - C64e(0x0c14100c08081c14), C64e(0x52f63152959563f6), - C64e(0x65af8c654646e9af), C64e(0x5ee2215e9d9d7fe2), - C64e(0x2878602830304878), C64e(0xa1f86ea13737cff8), - C64e(0x0f11140f0a0a1b11), C64e(0xb5c45eb52f2febc4), - C64e(0x091b1c090e0e151b), C64e(0x365a483624247e5a), - C64e(0x9bb6369b1b1badb6), C64e(0x3d47a53ddfdf9847), - C64e(0x266a8126cdcda76a), C64e(0x69bb9c694e4ef5bb), - C64e(0xcd4cfecd7f7f334c), C64e(0x9fbacf9feaea50ba), - C64e(0x1b2d241b12123f2d), C64e(0x9eb93a9e1d1da4b9), - C64e(0x749cb0745858c49c), C64e(0x2e72682e34344672), - C64e(0x2d776c2d36364177), C64e(0xb2cda3b2dcdc11cd), - C64e(0xee2973eeb4b49d29), C64e(0xfb16b6fb5b5b4d16), - C64e(0xf60153f6a4a4a501), C64e(0x4dd7ec4d7676a1d7), - C64e(0x61a37561b7b714a3), C64e(0xce49face7d7d3449), - C64e(0x7b8da47b5252df8d), C64e(0x3e42a13edddd9f42), - C64e(0x7193bc715e5ecd93), C64e(0x97a226971313b1a2), - C64e(0xf50457f5a6a6a204), C64e(0x68b86968b9b901b8), - C64e(0x0000000000000000), C64e(0x2c74992cc1c1b574), - C64e(0x60a080604040e0a0), C64e(0x1f21dd1fe3e3c221), - C64e(0xc843f2c879793a43), C64e(0xed2c77edb6b69a2c), - C64e(0xbed9b3bed4d40dd9), C64e(0x46ca01468d8d47ca), - C64e(0xd970ced967671770), C64e(0x4bdde44b7272afdd), - C64e(0xde7933de9494ed79), C64e(0xd4672bd49898ff67), - C64e(0xe8237be8b0b09323), C64e(0x4ade114a85855bde), - C64e(0x6bbd6d6bbbbb06bd), C64e(0x2a7e912ac5c5bb7e), - C64e(0xe5349ee54f4f7b34), C64e(0x163ac116ededd73a), - C64e(0xc55417c58686d254), C64e(0xd7622fd79a9af862), - C64e(0x55ffcc55666699ff), C64e(0x94a722941111b6a7), - C64e(0xcf4a0fcf8a8ac04a), C64e(0x1030c910e9e9d930), - C64e(0x060a080604040e0a), C64e(0x8198e781fefe6698), - C64e(0xf00b5bf0a0a0ab0b), C64e(0x44ccf0447878b4cc), - C64e(0xbad54aba2525f0d5), C64e(0xe33e96e34b4b753e), - C64e(0xf30e5ff3a2a2ac0e), C64e(0xfe19bafe5d5d4419), - C64e(0xc05b1bc08080db5b), C64e(0x8a850a8a05058085), - C64e(0xadec7ead3f3fd3ec), C64e(0xbcdf42bc2121fedf), - C64e(0x48d8e0487070a8d8), C64e(0x040cf904f1f1fd0c), - C64e(0xdf7ac6df6363197a), C64e(0xc158eec177772f58), - C64e(0x759f4575afaf309f), C64e(0x63a584634242e7a5), - C64e(0x3050403020207050), C64e(0x1a2ed11ae5e5cb2e), - C64e(0x0e12e10efdfdef12), C64e(0x6db7656dbfbf08b7), - C64e(0x4cd4194c818155d4), C64e(0x143c30141818243c), - C64e(0x355f4c352626795f), C64e(0x2f719d2fc3c3b271), - C64e(0xe13867e1bebe8638), C64e(0xa2fd6aa23535c8fd), - C64e(0xcc4f0bcc8888c74f), C64e(0x394b5c392e2e654b), - C64e(0x57f93d5793936af9), C64e(0xf20daaf25555580d), - C64e(0x829de382fcfc619d), C64e(0x47c9f4477a7ab3c9), - C64e(0xacef8bacc8c827ef), C64e(0xe7326fe7baba8832), - C64e(0x2b7d642b32324f7d), C64e(0x95a4d795e6e642a4), - C64e(0xa0fb9ba0c0c03bfb), C64e(0x98b332981919aab3), - C64e(0xd16827d19e9ef668), C64e(0x7f815d7fa3a32281), - C64e(0x66aa88664444eeaa), C64e(0x7e82a87e5454d682), - C64e(0xabe676ab3b3bdde6), C64e(0x839e16830b0b959e), - C64e(0xca4503ca8c8cc945), C64e(0x297b9529c7c7bc7b), - C64e(0xd36ed6d36b6b056e), C64e(0x3c44503c28286c44), - C64e(0x798b5579a7a72c8b), C64e(0xe23d63e2bcbc813d), - C64e(0x1d272c1d16163127), C64e(0x769a4176adad379a), - C64e(0x3b4dad3bdbdb964d), C64e(0x56fac85664649efa), - C64e(0x4ed2e84e7474a6d2), C64e(0x1e22281e14143622), - C64e(0xdb763fdb9292e476), C64e(0x0a1e180a0c0c121e), - C64e(0x6cb4906c4848fcb4), C64e(0xe4376be4b8b88f37), - C64e(0x5de7255d9f9f78e7), C64e(0x6eb2616ebdbd0fb2), - C64e(0xef2a86ef4343692a), C64e(0xa6f193a6c4c435f1), - C64e(0xa8e372a83939dae3), C64e(0xa4f762a43131c6f7), - C64e(0x3759bd37d3d38a59), C64e(0x8b86ff8bf2f27486), - C64e(0x3256b132d5d58356), C64e(0x43c50d438b8b4ec5), - C64e(0x59ebdc596e6e85eb), C64e(0xb7c2afb7dada18c2), - C64e(0x8c8f028c01018e8f), C64e(0x64ac7964b1b11dac), - C64e(0xd26d23d29c9cf16d), C64e(0xe03b92e04949723b), - C64e(0xb4c7abb4d8d81fc7), C64e(0xfa1543faacacb915), - C64e(0x0709fd07f3f3fa09), C64e(0x256f8525cfcfa06f), - C64e(0xafea8fafcaca20ea), C64e(0x8e89f38ef4f47d89), - C64e(0xe9208ee947476720), C64e(0x1828201810103828), - C64e(0xd564ded56f6f0b64), C64e(0x8883fb88f0f07383), - C64e(0x6fb1946f4a4afbb1), C64e(0x7296b8725c5cca96), - C64e(0x246c70243838546c), C64e(0xf108aef157575f08), - C64e(0xc752e6c773732152), C64e(0x51f33551979764f3), - C64e(0x23658d23cbcbae65), C64e(0x7c84597ca1a12584), - C64e(0x9cbfcb9ce8e857bf), C64e(0x21637c213e3e5d63), - C64e(0xdd7c37dd9696ea7c), C64e(0xdc7fc2dc61611e7f), - C64e(0x86911a860d0d9c91), C64e(0x85941e850f0f9b94), - C64e(0x90abdb90e0e04bab), C64e(0x42c6f8427c7cbac6), - C64e(0xc457e2c471712657), C64e(0xaae583aacccc29e5), - C64e(0xd8733bd89090e373), C64e(0x050f0c050606090f), - C64e(0x0103f501f7f7f403), C64e(0x123638121c1c2a36), - C64e(0xa3fe9fa3c2c23cfe), C64e(0x5fe1d45f6a6a8be1), - C64e(0xf91047f9aeaebe10), C64e(0xd06bd2d06969026b), - C64e(0x91a82e911717bfa8), C64e(0x58e82958999971e8), - C64e(0x276974273a3a5369), C64e(0xb9d04eb92727f7d0), - C64e(0x3848a938d9d99148), C64e(0x1335cd13ebebde35), - C64e(0xb3ce56b32b2be5ce), C64e(0x3355443322227755), - C64e(0xbbd6bfbbd2d204d6), C64e(0x70904970a9a93990), - C64e(0x89800e8907078780), C64e(0xa7f266a73333c1f2), - C64e(0xb6c15ab62d2decc1), C64e(0x226678223c3c5a66), - C64e(0x92ad2a921515b8ad), C64e(0x20608920c9c9a960), - C64e(0x49db154987875cdb), C64e(0xff1a4fffaaaab01a), - C64e(0x7888a0785050d888), C64e(0x7a8e517aa5a52b8e), - C64e(0x8f8a068f0303898a), C64e(0xf813b2f859594a13), - C64e(0x809b12800909929b), C64e(0x173934171a1a2339), - C64e(0xda75cada65651075), C64e(0x3153b531d7d78453), - C64e(0xc65113c68484d551), C64e(0xb8d3bbb8d0d003d3), - C64e(0xc35e1fc38282dc5e), C64e(0xb0cb52b02929e2cb), - C64e(0x7799b4775a5ac399), C64e(0x11333c111e1e2d33), - C64e(0xcb46f6cb7b7b3d46), C64e(0xfc1f4bfca8a8b71f), - C64e(0xd661dad66d6d0c61), C64e(0x3a4e583a2c2c624e) -}; - -static const sph_u64 T6[] = { - C64e(0xf4a5f497a5c6c632), C64e(0x978497eb84f8f86f), - C64e(0xb099b0c799eeee5e), C64e(0x8c8d8cf78df6f67a), - C64e(0x170d17e50dffffe8), C64e(0xdcbddcb7bdd6d60a), - C64e(0xc8b1c8a7b1dede16), C64e(0xfc54fc395491916d), - C64e(0xf050f0c050606090), C64e(0x0503050403020207), - C64e(0xe0a9e087a9cece2e), C64e(0x877d87ac7d5656d1), - C64e(0x2b192bd519e7e7cc), C64e(0xa662a67162b5b513), - C64e(0x31e6319ae64d4d7c), C64e(0xb59ab5c39aecec59), - C64e(0xcf45cf05458f8f40), C64e(0xbc9dbc3e9d1f1fa3), - C64e(0xc040c00940898949), C64e(0x928792ef87fafa68), - C64e(0x3f153fc515efefd0), C64e(0x26eb267febb2b294), - C64e(0x40c94007c98e8ece), C64e(0x1d0b1ded0bfbfbe6), - C64e(0x2fec2f82ec41416e), C64e(0xa967a97d67b3b31a), - C64e(0x1cfd1cbefd5f5f43), C64e(0x25ea258aea454560), - C64e(0xdabfda46bf2323f9), C64e(0x02f702a6f7535351), - C64e(0xa196a1d396e4e445), C64e(0xed5bed2d5b9b9b76), - C64e(0x5dc25deac2757528), C64e(0x241c24d91ce1e1c5), - C64e(0xe9aee97aae3d3dd4), C64e(0xbe6abe986a4c4cf2), - C64e(0xee5aeed85a6c6c82), C64e(0xc341c3fc417e7ebd), - C64e(0x060206f102f5f5f3), C64e(0xd14fd11d4f838352), - C64e(0xe45ce4d05c68688c), C64e(0x07f407a2f4515156), - C64e(0x5c345cb934d1d18d), C64e(0x180818e908f9f9e1), - C64e(0xae93aedf93e2e24c), C64e(0x9573954d73abab3e), - C64e(0xf553f5c453626297), C64e(0x413f41543f2a2a6b), - C64e(0x140c14100c08081c), C64e(0xf652f63152959563), - C64e(0xaf65af8c654646e9), C64e(0xe25ee2215e9d9d7f), - C64e(0x7828786028303048), C64e(0xf8a1f86ea13737cf), - C64e(0x110f11140f0a0a1b), C64e(0xc4b5c45eb52f2feb), - C64e(0x1b091b1c090e0e15), C64e(0x5a365a483624247e), - C64e(0xb69bb6369b1b1bad), C64e(0x473d47a53ddfdf98), - C64e(0x6a266a8126cdcda7), C64e(0xbb69bb9c694e4ef5), - C64e(0x4ccd4cfecd7f7f33), C64e(0xba9fbacf9feaea50), - C64e(0x2d1b2d241b12123f), C64e(0xb99eb93a9e1d1da4), - C64e(0x9c749cb0745858c4), C64e(0x722e72682e343446), - C64e(0x772d776c2d363641), C64e(0xcdb2cda3b2dcdc11), - C64e(0x29ee2973eeb4b49d), C64e(0x16fb16b6fb5b5b4d), - C64e(0x01f60153f6a4a4a5), C64e(0xd74dd7ec4d7676a1), - C64e(0xa361a37561b7b714), C64e(0x49ce49face7d7d34), - C64e(0x8d7b8da47b5252df), C64e(0x423e42a13edddd9f), - C64e(0x937193bc715e5ecd), C64e(0xa297a226971313b1), - C64e(0x04f50457f5a6a6a2), C64e(0xb868b86968b9b901), - C64e(0x0000000000000000), C64e(0x742c74992cc1c1b5), - C64e(0xa060a080604040e0), C64e(0x211f21dd1fe3e3c2), - C64e(0x43c843f2c879793a), C64e(0x2ced2c77edb6b69a), - C64e(0xd9bed9b3bed4d40d), C64e(0xca46ca01468d8d47), - C64e(0x70d970ced9676717), C64e(0xdd4bdde44b7272af), - C64e(0x79de7933de9494ed), C64e(0x67d4672bd49898ff), - C64e(0x23e8237be8b0b093), C64e(0xde4ade114a85855b), - C64e(0xbd6bbd6d6bbbbb06), C64e(0x7e2a7e912ac5c5bb), - C64e(0x34e5349ee54f4f7b), C64e(0x3a163ac116ededd7), - C64e(0x54c55417c58686d2), C64e(0x62d7622fd79a9af8), - C64e(0xff55ffcc55666699), C64e(0xa794a722941111b6), - C64e(0x4acf4a0fcf8a8ac0), C64e(0x301030c910e9e9d9), - C64e(0x0a060a080604040e), C64e(0x988198e781fefe66), - C64e(0x0bf00b5bf0a0a0ab), C64e(0xcc44ccf0447878b4), - C64e(0xd5bad54aba2525f0), C64e(0x3ee33e96e34b4b75), - C64e(0x0ef30e5ff3a2a2ac), C64e(0x19fe19bafe5d5d44), - C64e(0x5bc05b1bc08080db), C64e(0x858a850a8a050580), - C64e(0xecadec7ead3f3fd3), C64e(0xdfbcdf42bc2121fe), - C64e(0xd848d8e0487070a8), C64e(0x0c040cf904f1f1fd), - C64e(0x7adf7ac6df636319), C64e(0x58c158eec177772f), - C64e(0x9f759f4575afaf30), C64e(0xa563a584634242e7), - C64e(0x5030504030202070), C64e(0x2e1a2ed11ae5e5cb), - C64e(0x120e12e10efdfdef), C64e(0xb76db7656dbfbf08), - C64e(0xd44cd4194c818155), C64e(0x3c143c3014181824), - C64e(0x5f355f4c35262679), C64e(0x712f719d2fc3c3b2), - C64e(0x38e13867e1bebe86), C64e(0xfda2fd6aa23535c8), - C64e(0x4fcc4f0bcc8888c7), C64e(0x4b394b5c392e2e65), - C64e(0xf957f93d5793936a), C64e(0x0df20daaf2555558), - C64e(0x9d829de382fcfc61), C64e(0xc947c9f4477a7ab3), - C64e(0xefacef8bacc8c827), C64e(0x32e7326fe7baba88), - C64e(0x7d2b7d642b32324f), C64e(0xa495a4d795e6e642), - C64e(0xfba0fb9ba0c0c03b), C64e(0xb398b332981919aa), - C64e(0x68d16827d19e9ef6), C64e(0x817f815d7fa3a322), - C64e(0xaa66aa88664444ee), C64e(0x827e82a87e5454d6), - C64e(0xe6abe676ab3b3bdd), C64e(0x9e839e16830b0b95), - C64e(0x45ca4503ca8c8cc9), C64e(0x7b297b9529c7c7bc), - C64e(0x6ed36ed6d36b6b05), C64e(0x443c44503c28286c), - C64e(0x8b798b5579a7a72c), C64e(0x3de23d63e2bcbc81), - C64e(0x271d272c1d161631), C64e(0x9a769a4176adad37), - C64e(0x4d3b4dad3bdbdb96), C64e(0xfa56fac85664649e), - C64e(0xd24ed2e84e7474a6), C64e(0x221e22281e141436), - C64e(0x76db763fdb9292e4), C64e(0x1e0a1e180a0c0c12), - C64e(0xb46cb4906c4848fc), C64e(0x37e4376be4b8b88f), - C64e(0xe75de7255d9f9f78), C64e(0xb26eb2616ebdbd0f), - C64e(0x2aef2a86ef434369), C64e(0xf1a6f193a6c4c435), - C64e(0xe3a8e372a83939da), C64e(0xf7a4f762a43131c6), - C64e(0x593759bd37d3d38a), C64e(0x868b86ff8bf2f274), - C64e(0x563256b132d5d583), C64e(0xc543c50d438b8b4e), - C64e(0xeb59ebdc596e6e85), C64e(0xc2b7c2afb7dada18), - C64e(0x8f8c8f028c01018e), C64e(0xac64ac7964b1b11d), - C64e(0x6dd26d23d29c9cf1), C64e(0x3be03b92e0494972), - C64e(0xc7b4c7abb4d8d81f), C64e(0x15fa1543faacacb9), - C64e(0x090709fd07f3f3fa), C64e(0x6f256f8525cfcfa0), - C64e(0xeaafea8fafcaca20), C64e(0x898e89f38ef4f47d), - C64e(0x20e9208ee9474767), C64e(0x2818282018101038), - C64e(0x64d564ded56f6f0b), C64e(0x838883fb88f0f073), - C64e(0xb16fb1946f4a4afb), C64e(0x967296b8725c5cca), - C64e(0x6c246c7024383854), C64e(0x08f108aef157575f), - C64e(0x52c752e6c7737321), C64e(0xf351f33551979764), - C64e(0x6523658d23cbcbae), C64e(0x847c84597ca1a125), - C64e(0xbf9cbfcb9ce8e857), C64e(0x6321637c213e3e5d), - C64e(0x7cdd7c37dd9696ea), C64e(0x7fdc7fc2dc61611e), - C64e(0x9186911a860d0d9c), C64e(0x9485941e850f0f9b), - C64e(0xab90abdb90e0e04b), C64e(0xc642c6f8427c7cba), - C64e(0x57c457e2c4717126), C64e(0xe5aae583aacccc29), - C64e(0x73d8733bd89090e3), C64e(0x0f050f0c05060609), - C64e(0x030103f501f7f7f4), C64e(0x36123638121c1c2a), - C64e(0xfea3fe9fa3c2c23c), C64e(0xe15fe1d45f6a6a8b), - C64e(0x10f91047f9aeaebe), C64e(0x6bd06bd2d0696902), - C64e(0xa891a82e911717bf), C64e(0xe858e82958999971), - C64e(0x69276974273a3a53), C64e(0xd0b9d04eb92727f7), - C64e(0x483848a938d9d991), C64e(0x351335cd13ebebde), - C64e(0xceb3ce56b32b2be5), C64e(0x5533554433222277), - C64e(0xd6bbd6bfbbd2d204), C64e(0x9070904970a9a939), - C64e(0x8089800e89070787), C64e(0xf2a7f266a73333c1), - C64e(0xc1b6c15ab62d2dec), C64e(0x66226678223c3c5a), - C64e(0xad92ad2a921515b8), C64e(0x6020608920c9c9a9), - C64e(0xdb49db154987875c), C64e(0x1aff1a4fffaaaab0), - C64e(0x887888a0785050d8), C64e(0x8e7a8e517aa5a52b), - C64e(0x8a8f8a068f030389), C64e(0x13f813b2f859594a), - C64e(0x9b809b1280090992), C64e(0x39173934171a1a23), - C64e(0x75da75cada656510), C64e(0x533153b531d7d784), - C64e(0x51c65113c68484d5), C64e(0xd3b8d3bbb8d0d003), - C64e(0x5ec35e1fc38282dc), C64e(0xcbb0cb52b02929e2), - C64e(0x997799b4775a5ac3), C64e(0x3311333c111e1e2d), - C64e(0x46cb46f6cb7b7b3d), C64e(0x1ffc1f4bfca8a8b7), - C64e(0x61d661dad66d6d0c), C64e(0x4e3a4e583a2c2c62) -}; - -static const sph_u64 T7[] = { - C64e(0x32f4a5f497a5c6c6), C64e(0x6f978497eb84f8f8), - C64e(0x5eb099b0c799eeee), C64e(0x7a8c8d8cf78df6f6), - C64e(0xe8170d17e50dffff), C64e(0x0adcbddcb7bdd6d6), - C64e(0x16c8b1c8a7b1dede), C64e(0x6dfc54fc39549191), - C64e(0x90f050f0c0506060), C64e(0x0705030504030202), - C64e(0x2ee0a9e087a9cece), C64e(0xd1877d87ac7d5656), - C64e(0xcc2b192bd519e7e7), C64e(0x13a662a67162b5b5), - C64e(0x7c31e6319ae64d4d), C64e(0x59b59ab5c39aecec), - C64e(0x40cf45cf05458f8f), C64e(0xa3bc9dbc3e9d1f1f), - C64e(0x49c040c009408989), C64e(0x68928792ef87fafa), - C64e(0xd03f153fc515efef), C64e(0x9426eb267febb2b2), - C64e(0xce40c94007c98e8e), C64e(0xe61d0b1ded0bfbfb), - C64e(0x6e2fec2f82ec4141), C64e(0x1aa967a97d67b3b3), - C64e(0x431cfd1cbefd5f5f), C64e(0x6025ea258aea4545), - C64e(0xf9dabfda46bf2323), C64e(0x5102f702a6f75353), - C64e(0x45a196a1d396e4e4), C64e(0x76ed5bed2d5b9b9b), - C64e(0x285dc25deac27575), C64e(0xc5241c24d91ce1e1), - C64e(0xd4e9aee97aae3d3d), C64e(0xf2be6abe986a4c4c), - C64e(0x82ee5aeed85a6c6c), C64e(0xbdc341c3fc417e7e), - C64e(0xf3060206f102f5f5), C64e(0x52d14fd11d4f8383), - C64e(0x8ce45ce4d05c6868), C64e(0x5607f407a2f45151), - C64e(0x8d5c345cb934d1d1), C64e(0xe1180818e908f9f9), - C64e(0x4cae93aedf93e2e2), C64e(0x3e9573954d73abab), - C64e(0x97f553f5c4536262), C64e(0x6b413f41543f2a2a), - C64e(0x1c140c14100c0808), C64e(0x63f652f631529595), - C64e(0xe9af65af8c654646), C64e(0x7fe25ee2215e9d9d), - C64e(0x4878287860283030), C64e(0xcff8a1f86ea13737), - C64e(0x1b110f11140f0a0a), C64e(0xebc4b5c45eb52f2f), - C64e(0x151b091b1c090e0e), C64e(0x7e5a365a48362424), - C64e(0xadb69bb6369b1b1b), C64e(0x98473d47a53ddfdf), - C64e(0xa76a266a8126cdcd), C64e(0xf5bb69bb9c694e4e), - C64e(0x334ccd4cfecd7f7f), C64e(0x50ba9fbacf9feaea), - C64e(0x3f2d1b2d241b1212), C64e(0xa4b99eb93a9e1d1d), - C64e(0xc49c749cb0745858), C64e(0x46722e72682e3434), - C64e(0x41772d776c2d3636), C64e(0x11cdb2cda3b2dcdc), - C64e(0x9d29ee2973eeb4b4), C64e(0x4d16fb16b6fb5b5b), - C64e(0xa501f60153f6a4a4), C64e(0xa1d74dd7ec4d7676), - C64e(0x14a361a37561b7b7), C64e(0x3449ce49face7d7d), - C64e(0xdf8d7b8da47b5252), C64e(0x9f423e42a13edddd), - C64e(0xcd937193bc715e5e), C64e(0xb1a297a226971313), - C64e(0xa204f50457f5a6a6), C64e(0x01b868b86968b9b9), - C64e(0x0000000000000000), C64e(0xb5742c74992cc1c1), - C64e(0xe0a060a080604040), C64e(0xc2211f21dd1fe3e3), - C64e(0x3a43c843f2c87979), C64e(0x9a2ced2c77edb6b6), - C64e(0x0dd9bed9b3bed4d4), C64e(0x47ca46ca01468d8d), - C64e(0x1770d970ced96767), C64e(0xafdd4bdde44b7272), - C64e(0xed79de7933de9494), C64e(0xff67d4672bd49898), - C64e(0x9323e8237be8b0b0), C64e(0x5bde4ade114a8585), - C64e(0x06bd6bbd6d6bbbbb), C64e(0xbb7e2a7e912ac5c5), - C64e(0x7b34e5349ee54f4f), C64e(0xd73a163ac116eded), - C64e(0xd254c55417c58686), C64e(0xf862d7622fd79a9a), - C64e(0x99ff55ffcc556666), C64e(0xb6a794a722941111), - C64e(0xc04acf4a0fcf8a8a), C64e(0xd9301030c910e9e9), - C64e(0x0e0a060a08060404), C64e(0x66988198e781fefe), - C64e(0xab0bf00b5bf0a0a0), C64e(0xb4cc44ccf0447878), - C64e(0xf0d5bad54aba2525), C64e(0x753ee33e96e34b4b), - C64e(0xac0ef30e5ff3a2a2), C64e(0x4419fe19bafe5d5d), - C64e(0xdb5bc05b1bc08080), C64e(0x80858a850a8a0505), - C64e(0xd3ecadec7ead3f3f), C64e(0xfedfbcdf42bc2121), - C64e(0xa8d848d8e0487070), C64e(0xfd0c040cf904f1f1), - C64e(0x197adf7ac6df6363), C64e(0x2f58c158eec17777), - C64e(0x309f759f4575afaf), C64e(0xe7a563a584634242), - C64e(0x7050305040302020), C64e(0xcb2e1a2ed11ae5e5), - C64e(0xef120e12e10efdfd), C64e(0x08b76db7656dbfbf), - C64e(0x55d44cd4194c8181), C64e(0x243c143c30141818), - C64e(0x795f355f4c352626), C64e(0xb2712f719d2fc3c3), - C64e(0x8638e13867e1bebe), C64e(0xc8fda2fd6aa23535), - C64e(0xc74fcc4f0bcc8888), C64e(0x654b394b5c392e2e), - C64e(0x6af957f93d579393), C64e(0x580df20daaf25555), - C64e(0x619d829de382fcfc), C64e(0xb3c947c9f4477a7a), - C64e(0x27efacef8bacc8c8), C64e(0x8832e7326fe7baba), - C64e(0x4f7d2b7d642b3232), C64e(0x42a495a4d795e6e6), - C64e(0x3bfba0fb9ba0c0c0), C64e(0xaab398b332981919), - C64e(0xf668d16827d19e9e), C64e(0x22817f815d7fa3a3), - C64e(0xeeaa66aa88664444), C64e(0xd6827e82a87e5454), - C64e(0xdde6abe676ab3b3b), C64e(0x959e839e16830b0b), - C64e(0xc945ca4503ca8c8c), C64e(0xbc7b297b9529c7c7), - C64e(0x056ed36ed6d36b6b), C64e(0x6c443c44503c2828), - C64e(0x2c8b798b5579a7a7), C64e(0x813de23d63e2bcbc), - C64e(0x31271d272c1d1616), C64e(0x379a769a4176adad), - C64e(0x964d3b4dad3bdbdb), C64e(0x9efa56fac8566464), - C64e(0xa6d24ed2e84e7474), C64e(0x36221e22281e1414), - C64e(0xe476db763fdb9292), C64e(0x121e0a1e180a0c0c), - C64e(0xfcb46cb4906c4848), C64e(0x8f37e4376be4b8b8), - C64e(0x78e75de7255d9f9f), C64e(0x0fb26eb2616ebdbd), - C64e(0x692aef2a86ef4343), C64e(0x35f1a6f193a6c4c4), - C64e(0xdae3a8e372a83939), C64e(0xc6f7a4f762a43131), - C64e(0x8a593759bd37d3d3), C64e(0x74868b86ff8bf2f2), - C64e(0x83563256b132d5d5), C64e(0x4ec543c50d438b8b), - C64e(0x85eb59ebdc596e6e), C64e(0x18c2b7c2afb7dada), - C64e(0x8e8f8c8f028c0101), C64e(0x1dac64ac7964b1b1), - C64e(0xf16dd26d23d29c9c), C64e(0x723be03b92e04949), - C64e(0x1fc7b4c7abb4d8d8), C64e(0xb915fa1543faacac), - C64e(0xfa090709fd07f3f3), C64e(0xa06f256f8525cfcf), - C64e(0x20eaafea8fafcaca), C64e(0x7d898e89f38ef4f4), - C64e(0x6720e9208ee94747), C64e(0x3828182820181010), - C64e(0x0b64d564ded56f6f), C64e(0x73838883fb88f0f0), - C64e(0xfbb16fb1946f4a4a), C64e(0xca967296b8725c5c), - C64e(0x546c246c70243838), C64e(0x5f08f108aef15757), - C64e(0x2152c752e6c77373), C64e(0x64f351f335519797), - C64e(0xae6523658d23cbcb), C64e(0x25847c84597ca1a1), - C64e(0x57bf9cbfcb9ce8e8), C64e(0x5d6321637c213e3e), - C64e(0xea7cdd7c37dd9696), C64e(0x1e7fdc7fc2dc6161), - C64e(0x9c9186911a860d0d), C64e(0x9b9485941e850f0f), - C64e(0x4bab90abdb90e0e0), C64e(0xbac642c6f8427c7c), - C64e(0x2657c457e2c47171), C64e(0x29e5aae583aacccc), - C64e(0xe373d8733bd89090), C64e(0x090f050f0c050606), - C64e(0xf4030103f501f7f7), C64e(0x2a36123638121c1c), - C64e(0x3cfea3fe9fa3c2c2), C64e(0x8be15fe1d45f6a6a), - C64e(0xbe10f91047f9aeae), C64e(0x026bd06bd2d06969), - C64e(0xbfa891a82e911717), C64e(0x71e858e829589999), - C64e(0x5369276974273a3a), C64e(0xf7d0b9d04eb92727), - C64e(0x91483848a938d9d9), C64e(0xde351335cd13ebeb), - C64e(0xe5ceb3ce56b32b2b), C64e(0x7755335544332222), - C64e(0x04d6bbd6bfbbd2d2), C64e(0x399070904970a9a9), - C64e(0x878089800e890707), C64e(0xc1f2a7f266a73333), - C64e(0xecc1b6c15ab62d2d), C64e(0x5a66226678223c3c), - C64e(0xb8ad92ad2a921515), C64e(0xa96020608920c9c9), - C64e(0x5cdb49db15498787), C64e(0xb01aff1a4fffaaaa), - C64e(0xd8887888a0785050), C64e(0x2b8e7a8e517aa5a5), - C64e(0x898a8f8a068f0303), C64e(0x4a13f813b2f85959), - C64e(0x929b809b12800909), C64e(0x2339173934171a1a), - C64e(0x1075da75cada6565), C64e(0x84533153b531d7d7), - C64e(0xd551c65113c68484), C64e(0x03d3b8d3bbb8d0d0), - C64e(0xdc5ec35e1fc38282), C64e(0xe2cbb0cb52b02929), - C64e(0xc3997799b4775a5a), C64e(0x2d3311333c111e1e), - C64e(0x3d46cb46f6cb7b7b), C64e(0xb71ffc1f4bfca8a8), - C64e(0x0c61d661dad66d6d), C64e(0x624e3a4e583a2c2c) -}; - -#endif - -#define DECL_STATE_SMALL \ - sph_u64 H[8]; - -#define READ_STATE_SMALL(sc) do { \ - memcpy(H, (sc)->state.wide, sizeof H); \ - } while (0) - -#define WRITE_STATE_SMALL(sc) do { \ - memcpy((sc)->state.wide, H, sizeof H); \ - } while (0) - -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define RSTT(d, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ - t[d] = T0[B64_0(a[b0])] \ - ^ R64(T0[B64_1(a[b1])], 8) \ - ^ R64(T0[B64_2(a[b2])], 16) \ - ^ R64(T0[B64_3(a[b3])], 24) \ - ^ T4[B64_4(a[b4])] \ - ^ R64(T4[B64_5(a[b5])], 8) \ - ^ R64(T4[B64_6(a[b6])], 16) \ - ^ R64(T4[B64_7(a[b7])], 24); \ - } while (0) - -#else - -#define RSTT(d, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ - t[d] = T0[B64_0(a[b0])] \ - ^ T1[B64_1(a[b1])] \ - ^ T2[B64_2(a[b2])] \ - ^ T3[B64_3(a[b3])] \ - ^ T4[B64_4(a[b4])] \ - ^ T5[B64_5(a[b5])] \ - ^ T6[B64_6(a[b6])] \ - ^ T7[B64_7(a[b7])]; \ - } while (0) - -#endif - -#define ROUND_SMALL_P(a, r) do { \ - sph_u64 t[8]; \ - a[0] ^= PC64(0x00, r); \ - a[1] ^= PC64(0x10, r); \ - a[2] ^= PC64(0x20, r); \ - a[3] ^= PC64(0x30, r); \ - a[4] ^= PC64(0x40, r); \ - a[5] ^= PC64(0x50, r); \ - a[6] ^= PC64(0x60, r); \ - a[7] ^= PC64(0x70, r); \ - RSTT(0, a, 0, 1, 2, 3, 4, 5, 6, 7); \ - RSTT(1, a, 1, 2, 3, 4, 5, 6, 7, 0); \ - RSTT(2, a, 2, 3, 4, 5, 6, 7, 0, 1); \ - RSTT(3, a, 3, 4, 5, 6, 7, 0, 1, 2); \ - RSTT(4, a, 4, 5, 6, 7, 0, 1, 2, 3); \ - RSTT(5, a, 5, 6, 7, 0, 1, 2, 3, 4); \ - RSTT(6, a, 6, 7, 0, 1, 2, 3, 4, 5); \ - RSTT(7, a, 7, 0, 1, 2, 3, 4, 5, 6); \ - a[0] = t[0]; \ - a[1] = t[1]; \ - a[2] = t[2]; \ - a[3] = t[3]; \ - a[4] = t[4]; \ - a[5] = t[5]; \ - a[6] = t[6]; \ - a[7] = t[7]; \ - } while (0) - -#define ROUND_SMALL_Q(a, r) do { \ - sph_u64 t[8]; \ - a[0] ^= QC64(0x00, r); \ - a[1] ^= QC64(0x10, r); \ - a[2] ^= QC64(0x20, r); \ - a[3] ^= QC64(0x30, r); \ - a[4] ^= QC64(0x40, r); \ - a[5] ^= QC64(0x50, r); \ - a[6] ^= QC64(0x60, r); \ - a[7] ^= QC64(0x70, r); \ - RSTT(0, a, 1, 3, 5, 7, 0, 2, 4, 6); \ - RSTT(1, a, 2, 4, 6, 0, 1, 3, 5, 7); \ - RSTT(2, a, 3, 5, 7, 1, 2, 4, 6, 0); \ - RSTT(3, a, 4, 6, 0, 2, 3, 5, 7, 1); \ - RSTT(4, a, 5, 7, 1, 3, 4, 6, 0, 2); \ - RSTT(5, a, 6, 0, 2, 4, 5, 7, 1, 3); \ - RSTT(6, a, 7, 1, 3, 5, 6, 0, 2, 4); \ - RSTT(7, a, 0, 2, 4, 6, 7, 1, 3, 5); \ - a[0] = t[0]; \ - a[1] = t[1]; \ - a[2] = t[2]; \ - a[3] = t[3]; \ - a[4] = t[4]; \ - a[5] = t[5]; \ - a[6] = t[6]; \ - a[7] = t[7]; \ - } while (0) - -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define PERM_SMALL_P(a) do { \ - int r; \ - for (r = 0; r < 10; r ++) \ - ROUND_SMALL_P(a, r); \ - } while (0) - -#define PERM_SMALL_Q(a) do { \ - int r; \ - for (r = 0; r < 10; r ++) \ - ROUND_SMALL_Q(a, r); \ - } while (0) - -#else - -/* - * Apparently, unrolling more than that confuses GCC, resulting in - * lower performance, even though L1 cache would be no problem. - */ -#define PERM_SMALL_P(a) do { \ - int r; \ - for (r = 0; r < 10; r += 2) { \ - ROUND_SMALL_P(a, r + 0); \ - ROUND_SMALL_P(a, r + 1); \ - } \ - } while (0) - -#define PERM_SMALL_Q(a) do { \ - int r; \ - for (r = 0; r < 10; r += 2) { \ - ROUND_SMALL_Q(a, r + 0); \ - ROUND_SMALL_Q(a, r + 1); \ - } \ - } while (0) - -#endif - -#define COMPRESS_SMALL do { \ - sph_u64 g[8], m[8]; \ - size_t u; \ - for (u = 0; u < 8; u ++) { \ - m[u] = dec64e_aligned(buf + (u << 3)); \ - g[u] = m[u] ^ H[u]; \ - } \ - PERM_SMALL_P(g); \ - PERM_SMALL_Q(m); \ - for (u = 0; u < 8; u ++) \ - H[u] ^= g[u] ^ m[u]; \ - } while (0) - -#define FINAL_SMALL do { \ - sph_u64 x[8]; \ - size_t u; \ - memcpy(x, H, sizeof x); \ - PERM_SMALL_P(x); \ - for (u = 0; u < 8; u ++) \ - H[u] ^= x[u]; \ - } while (0) - -#define DECL_STATE_BIG \ - sph_u64 H[16]; - -#define READ_STATE_BIG(sc) do { \ - memcpy(H, (sc)->state.wide, sizeof H); \ - } while (0) - -#define WRITE_STATE_BIG(sc) do { \ - memcpy((sc)->state.wide, H, sizeof H); \ - } while (0) - -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define RBTT(d, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ - t[d] = T0[B64_0(a[b0])] \ - ^ R64(T0[B64_1(a[b1])], 8) \ - ^ R64(T0[B64_2(a[b2])], 16) \ - ^ R64(T0[B64_3(a[b3])], 24) \ - ^ T4[B64_4(a[b4])] \ - ^ R64(T4[B64_5(a[b5])], 8) \ - ^ R64(T4[B64_6(a[b6])], 16) \ - ^ R64(T4[B64_7(a[b7])], 24); \ - } while (0) - -#else - -#define RBTT(d, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ - t[d] = T0[B64_0(a[b0])] \ - ^ T1[B64_1(a[b1])] \ - ^ T2[B64_2(a[b2])] \ - ^ T3[B64_3(a[b3])] \ - ^ T4[B64_4(a[b4])] \ - ^ T5[B64_5(a[b5])] \ - ^ T6[B64_6(a[b6])] \ - ^ T7[B64_7(a[b7])]; \ - } while (0) - -#endif - -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define ROUND_BIG_P(a, r) do { \ - sph_u64 t[16]; \ - size_t u; \ - a[0x0] ^= PC64(0x00, r); \ - a[0x1] ^= PC64(0x10, r); \ - a[0x2] ^= PC64(0x20, r); \ - a[0x3] ^= PC64(0x30, r); \ - a[0x4] ^= PC64(0x40, r); \ - a[0x5] ^= PC64(0x50, r); \ - a[0x6] ^= PC64(0x60, r); \ - a[0x7] ^= PC64(0x70, r); \ - a[0x8] ^= PC64(0x80, r); \ - a[0x9] ^= PC64(0x90, r); \ - a[0xA] ^= PC64(0xA0, r); \ - a[0xB] ^= PC64(0xB0, r); \ - a[0xC] ^= PC64(0xC0, r); \ - a[0xD] ^= PC64(0xD0, r); \ - a[0xE] ^= PC64(0xE0, r); \ - a[0xF] ^= PC64(0xF0, r); \ - for (u = 0; u < 16; u += 4) { \ - RBTT(u + 0, a, u + 0, (u + 1) & 0xF, \ - (u + 2) & 0xF, (u + 3) & 0xF, (u + 4) & 0xF, \ - (u + 5) & 0xF, (u + 6) & 0xF, (u + 11) & 0xF); \ - RBTT(u + 1, a, u + 1, (u + 2) & 0xF, \ - (u + 3) & 0xF, (u + 4) & 0xF, (u + 5) & 0xF, \ - (u + 6) & 0xF, (u + 7) & 0xF, (u + 12) & 0xF); \ - RBTT(u + 2, a, u + 2, (u + 3) & 0xF, \ - (u + 4) & 0xF, (u + 5) & 0xF, (u + 6) & 0xF, \ - (u + 7) & 0xF, (u + 8) & 0xF, (u + 13) & 0xF); \ - RBTT(u + 3, a, u + 3, (u + 4) & 0xF, \ - (u + 5) & 0xF, (u + 6) & 0xF, (u + 7) & 0xF, \ - (u + 8) & 0xF, (u + 9) & 0xF, (u + 14) & 0xF); \ - } \ - memcpy(a, t, sizeof t); \ - } while (0) - -#define ROUND_BIG_Q(a, r) do { \ - sph_u64 t[16]; \ - size_t u; \ - a[0x0] ^= QC64(0x00, r); \ - a[0x1] ^= QC64(0x10, r); \ - a[0x2] ^= QC64(0x20, r); \ - a[0x3] ^= QC64(0x30, r); \ - a[0x4] ^= QC64(0x40, r); \ - a[0x5] ^= QC64(0x50, r); \ - a[0x6] ^= QC64(0x60, r); \ - a[0x7] ^= QC64(0x70, r); \ - a[0x8] ^= QC64(0x80, r); \ - a[0x9] ^= QC64(0x90, r); \ - a[0xA] ^= QC64(0xA0, r); \ - a[0xB] ^= QC64(0xB0, r); \ - a[0xC] ^= QC64(0xC0, r); \ - a[0xD] ^= QC64(0xD0, r); \ - a[0xE] ^= QC64(0xE0, r); \ - a[0xF] ^= QC64(0xF0, r); \ - for (u = 0; u < 16; u += 4) { \ - RBTT(u + 0, a, (u + 1) & 0xF, (u + 3) & 0xF, \ - (u + 5) & 0xF, (u + 11) & 0xF, (u + 0) & 0xF, \ - (u + 2) & 0xF, (u + 4) & 0xF, (u + 6) & 0xF); \ - RBTT(u + 1, a, (u + 2) & 0xF, (u + 4) & 0xF, \ - (u + 6) & 0xF, (u + 12) & 0xF, (u + 1) & 0xF, \ - (u + 3) & 0xF, (u + 5) & 0xF, (u + 7) & 0xF); \ - RBTT(u + 2, a, (u + 3) & 0xF, (u + 5) & 0xF, \ - (u + 7) & 0xF, (u + 13) & 0xF, (u + 2) & 0xF, \ - (u + 4) & 0xF, (u + 6) & 0xF, (u + 8) & 0xF); \ - RBTT(u + 3, a, (u + 4) & 0xF, (u + 6) & 0xF, \ - (u + 8) & 0xF, (u + 14) & 0xF, (u + 3) & 0xF, \ - (u + 5) & 0xF, (u + 7) & 0xF, (u + 9) & 0xF); \ - } \ - memcpy(a, t, sizeof t); \ - } while (0) - -#else - -#define ROUND_BIG_P(a, r) do { \ - sph_u64 t[16]; \ - a[0x0] ^= PC64(0x00, r); \ - a[0x1] ^= PC64(0x10, r); \ - a[0x2] ^= PC64(0x20, r); \ - a[0x3] ^= PC64(0x30, r); \ - a[0x4] ^= PC64(0x40, r); \ - a[0x5] ^= PC64(0x50, r); \ - a[0x6] ^= PC64(0x60, r); \ - a[0x7] ^= PC64(0x70, r); \ - a[0x8] ^= PC64(0x80, r); \ - a[0x9] ^= PC64(0x90, r); \ - a[0xA] ^= PC64(0xA0, r); \ - a[0xB] ^= PC64(0xB0, r); \ - a[0xC] ^= PC64(0xC0, r); \ - a[0xD] ^= PC64(0xD0, r); \ - a[0xE] ^= PC64(0xE0, r); \ - a[0xF] ^= PC64(0xF0, r); \ - RBTT(0x0, a, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xB); \ - RBTT(0x1, a, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0xC); \ - RBTT(0x2, a, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0xD); \ - RBTT(0x3, a, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xE); \ - RBTT(0x4, a, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xF); \ - RBTT(0x5, a, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0x0); \ - RBTT(0x6, a, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0x1); \ - RBTT(0x7, a, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0x2); \ - RBTT(0x8, a, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0x3); \ - RBTT(0x9, a, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x4); \ - RBTT(0xA, a, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 0x5); \ - RBTT(0xB, a, 0xB, 0xC, 0xD, 0xE, 0xF, 0x0, 0x1, 0x6); \ - RBTT(0xC, a, 0xC, 0xD, 0xE, 0xF, 0x0, 0x1, 0x2, 0x7); \ - RBTT(0xD, a, 0xD, 0xE, 0xF, 0x0, 0x1, 0x2, 0x3, 0x8); \ - RBTT(0xE, a, 0xE, 0xF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x9); \ - RBTT(0xF, a, 0xF, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0xA); \ - a[0x0] = t[0x0]; \ - a[0x1] = t[0x1]; \ - a[0x2] = t[0x2]; \ - a[0x3] = t[0x3]; \ - a[0x4] = t[0x4]; \ - a[0x5] = t[0x5]; \ - a[0x6] = t[0x6]; \ - a[0x7] = t[0x7]; \ - a[0x8] = t[0x8]; \ - a[0x9] = t[0x9]; \ - a[0xA] = t[0xA]; \ - a[0xB] = t[0xB]; \ - a[0xC] = t[0xC]; \ - a[0xD] = t[0xD]; \ - a[0xE] = t[0xE]; \ - a[0xF] = t[0xF]; \ - } while (0) - -#define ROUND_BIG_Q(a, r) do { \ - sph_u64 t[16]; \ - a[0x0] ^= QC64(0x00, r); \ - a[0x1] ^= QC64(0x10, r); \ - a[0x2] ^= QC64(0x20, r); \ - a[0x3] ^= QC64(0x30, r); \ - a[0x4] ^= QC64(0x40, r); \ - a[0x5] ^= QC64(0x50, r); \ - a[0x6] ^= QC64(0x60, r); \ - a[0x7] ^= QC64(0x70, r); \ - a[0x8] ^= QC64(0x80, r); \ - a[0x9] ^= QC64(0x90, r); \ - a[0xA] ^= QC64(0xA0, r); \ - a[0xB] ^= QC64(0xB0, r); \ - a[0xC] ^= QC64(0xC0, r); \ - a[0xD] ^= QC64(0xD0, r); \ - a[0xE] ^= QC64(0xE0, r); \ - a[0xF] ^= QC64(0xF0, r); \ - RBTT(0x0, a, 0x1, 0x3, 0x5, 0xB, 0x0, 0x2, 0x4, 0x6); \ - RBTT(0x1, a, 0x2, 0x4, 0x6, 0xC, 0x1, 0x3, 0x5, 0x7); \ - RBTT(0x2, a, 0x3, 0x5, 0x7, 0xD, 0x2, 0x4, 0x6, 0x8); \ - RBTT(0x3, a, 0x4, 0x6, 0x8, 0xE, 0x3, 0x5, 0x7, 0x9); \ - RBTT(0x4, a, 0x5, 0x7, 0x9, 0xF, 0x4, 0x6, 0x8, 0xA); \ - RBTT(0x5, a, 0x6, 0x8, 0xA, 0x0, 0x5, 0x7, 0x9, 0xB); \ - RBTT(0x6, a, 0x7, 0x9, 0xB, 0x1, 0x6, 0x8, 0xA, 0xC); \ - RBTT(0x7, a, 0x8, 0xA, 0xC, 0x2, 0x7, 0x9, 0xB, 0xD); \ - RBTT(0x8, a, 0x9, 0xB, 0xD, 0x3, 0x8, 0xA, 0xC, 0xE); \ - RBTT(0x9, a, 0xA, 0xC, 0xE, 0x4, 0x9, 0xB, 0xD, 0xF); \ - RBTT(0xA, a, 0xB, 0xD, 0xF, 0x5, 0xA, 0xC, 0xE, 0x0); \ - RBTT(0xB, a, 0xC, 0xE, 0x0, 0x6, 0xB, 0xD, 0xF, 0x1); \ - RBTT(0xC, a, 0xD, 0xF, 0x1, 0x7, 0xC, 0xE, 0x0, 0x2); \ - RBTT(0xD, a, 0xE, 0x0, 0x2, 0x8, 0xD, 0xF, 0x1, 0x3); \ - RBTT(0xE, a, 0xF, 0x1, 0x3, 0x9, 0xE, 0x0, 0x2, 0x4); \ - RBTT(0xF, a, 0x0, 0x2, 0x4, 0xA, 0xF, 0x1, 0x3, 0x5); \ - a[0x0] = t[0x0]; \ - a[0x1] = t[0x1]; \ - a[0x2] = t[0x2]; \ - a[0x3] = t[0x3]; \ - a[0x4] = t[0x4]; \ - a[0x5] = t[0x5]; \ - a[0x6] = t[0x6]; \ - a[0x7] = t[0x7]; \ - a[0x8] = t[0x8]; \ - a[0x9] = t[0x9]; \ - a[0xA] = t[0xA]; \ - a[0xB] = t[0xB]; \ - a[0xC] = t[0xC]; \ - a[0xD] = t[0xD]; \ - a[0xE] = t[0xE]; \ - a[0xF] = t[0xF]; \ - } while (0) - -#endif - -#define PERM_BIG_P(a) do { \ - int r; \ - for (r = 0; r < 14; r += 2) { \ - ROUND_BIG_P(a, r + 0); \ - ROUND_BIG_P(a, r + 1); \ - } \ - } while (0) - -#define PERM_BIG_Q(a) do { \ - int r; \ - for (r = 0; r < 14; r += 2) { \ - ROUND_BIG_Q(a, r + 0); \ - ROUND_BIG_Q(a, r + 1); \ - } \ - } while (0) - -/* obsolete -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define COMPRESS_BIG do { \ - sph_u64 g[16], m[16], *ya; \ - const sph_u64 *yc; \ - size_t u; \ - int i; \ - for (u = 0; u < 16; u ++) { \ - m[u] = dec64e_aligned(buf + (u << 3)); \ - g[u] = m[u] ^ H[u]; \ - } \ - ya = g; \ - yc = CP; \ - for (i = 0; i < 2; i ++) { \ - PERM_BIG(ya, yc); \ - ya = m; \ - yc = CQ; \ - } \ - for (u = 0; u < 16; u ++) { \ - H[u] ^= g[u] ^ m[u]; \ - } \ - } while (0) - -#else -*/ - -#define COMPRESS_BIG do { \ - sph_u64 g[16], m[16]; \ - size_t u; \ - for (u = 0; u < 16; u ++) { \ - m[u] = dec64e_aligned(buf + (u << 3)); \ - g[u] = m[u] ^ H[u]; \ - } \ - PERM_BIG_P(g); \ - PERM_BIG_Q(m); \ - for (u = 0; u < 16; u ++) { \ - H[u] ^= g[u] ^ m[u]; \ - } \ - } while (0) - -/* obsolete -#endif -*/ - -#define FINAL_BIG do { \ - sph_u64 x[16]; \ - size_t u; \ - memcpy(x, H, sizeof x); \ - PERM_BIG_P(x); \ - for (u = 0; u < 16; u ++) \ - H[u] ^= x[u]; \ - } while (0) - -#else - -static const sph_u32 T0up[] = { - C32e(0xc632f4a5), C32e(0xf86f9784), C32e(0xee5eb099), C32e(0xf67a8c8d), - C32e(0xffe8170d), C32e(0xd60adcbd), C32e(0xde16c8b1), C32e(0x916dfc54), - C32e(0x6090f050), C32e(0x02070503), C32e(0xce2ee0a9), C32e(0x56d1877d), - C32e(0xe7cc2b19), C32e(0xb513a662), C32e(0x4d7c31e6), C32e(0xec59b59a), - C32e(0x8f40cf45), C32e(0x1fa3bc9d), C32e(0x8949c040), C32e(0xfa689287), - C32e(0xefd03f15), C32e(0xb29426eb), C32e(0x8ece40c9), C32e(0xfbe61d0b), - C32e(0x416e2fec), C32e(0xb31aa967), C32e(0x5f431cfd), C32e(0x456025ea), - C32e(0x23f9dabf), C32e(0x535102f7), C32e(0xe445a196), C32e(0x9b76ed5b), - C32e(0x75285dc2), C32e(0xe1c5241c), C32e(0x3dd4e9ae), C32e(0x4cf2be6a), - C32e(0x6c82ee5a), C32e(0x7ebdc341), C32e(0xf5f30602), C32e(0x8352d14f), - C32e(0x688ce45c), C32e(0x515607f4), C32e(0xd18d5c34), C32e(0xf9e11808), - C32e(0xe24cae93), C32e(0xab3e9573), C32e(0x6297f553), C32e(0x2a6b413f), - C32e(0x081c140c), C32e(0x9563f652), C32e(0x46e9af65), C32e(0x9d7fe25e), - C32e(0x30487828), C32e(0x37cff8a1), C32e(0x0a1b110f), C32e(0x2febc4b5), - C32e(0x0e151b09), C32e(0x247e5a36), C32e(0x1badb69b), C32e(0xdf98473d), - C32e(0xcda76a26), C32e(0x4ef5bb69), C32e(0x7f334ccd), C32e(0xea50ba9f), - C32e(0x123f2d1b), C32e(0x1da4b99e), C32e(0x58c49c74), C32e(0x3446722e), - C32e(0x3641772d), C32e(0xdc11cdb2), C32e(0xb49d29ee), C32e(0x5b4d16fb), - C32e(0xa4a501f6), C32e(0x76a1d74d), C32e(0xb714a361), C32e(0x7d3449ce), - C32e(0x52df8d7b), C32e(0xdd9f423e), C32e(0x5ecd9371), C32e(0x13b1a297), - C32e(0xa6a204f5), C32e(0xb901b868), C32e(0x00000000), C32e(0xc1b5742c), - C32e(0x40e0a060), C32e(0xe3c2211f), C32e(0x793a43c8), C32e(0xb69a2ced), - C32e(0xd40dd9be), C32e(0x8d47ca46), C32e(0x671770d9), C32e(0x72afdd4b), - C32e(0x94ed79de), C32e(0x98ff67d4), C32e(0xb09323e8), C32e(0x855bde4a), - C32e(0xbb06bd6b), C32e(0xc5bb7e2a), C32e(0x4f7b34e5), C32e(0xedd73a16), - C32e(0x86d254c5), C32e(0x9af862d7), C32e(0x6699ff55), C32e(0x11b6a794), - C32e(0x8ac04acf), C32e(0xe9d93010), C32e(0x040e0a06), C32e(0xfe669881), - C32e(0xa0ab0bf0), C32e(0x78b4cc44), C32e(0x25f0d5ba), C32e(0x4b753ee3), - C32e(0xa2ac0ef3), C32e(0x5d4419fe), C32e(0x80db5bc0), C32e(0x0580858a), - C32e(0x3fd3ecad), C32e(0x21fedfbc), C32e(0x70a8d848), C32e(0xf1fd0c04), - C32e(0x63197adf), C32e(0x772f58c1), C32e(0xaf309f75), C32e(0x42e7a563), - C32e(0x20705030), C32e(0xe5cb2e1a), C32e(0xfdef120e), C32e(0xbf08b76d), - C32e(0x8155d44c), C32e(0x18243c14), C32e(0x26795f35), C32e(0xc3b2712f), - C32e(0xbe8638e1), C32e(0x35c8fda2), C32e(0x88c74fcc), C32e(0x2e654b39), - C32e(0x936af957), C32e(0x55580df2), C32e(0xfc619d82), C32e(0x7ab3c947), - C32e(0xc827efac), C32e(0xba8832e7), C32e(0x324f7d2b), C32e(0xe642a495), - C32e(0xc03bfba0), C32e(0x19aab398), C32e(0x9ef668d1), C32e(0xa322817f), - C32e(0x44eeaa66), C32e(0x54d6827e), C32e(0x3bdde6ab), C32e(0x0b959e83), - C32e(0x8cc945ca), C32e(0xc7bc7b29), C32e(0x6b056ed3), C32e(0x286c443c), - C32e(0xa72c8b79), C32e(0xbc813de2), C32e(0x1631271d), C32e(0xad379a76), - C32e(0xdb964d3b), C32e(0x649efa56), C32e(0x74a6d24e), C32e(0x1436221e), - C32e(0x92e476db), C32e(0x0c121e0a), C32e(0x48fcb46c), C32e(0xb88f37e4), - C32e(0x9f78e75d), C32e(0xbd0fb26e), C32e(0x43692aef), C32e(0xc435f1a6), - C32e(0x39dae3a8), C32e(0x31c6f7a4), C32e(0xd38a5937), C32e(0xf274868b), - C32e(0xd5835632), C32e(0x8b4ec543), C32e(0x6e85eb59), C32e(0xda18c2b7), - C32e(0x018e8f8c), C32e(0xb11dac64), C32e(0x9cf16dd2), C32e(0x49723be0), - C32e(0xd81fc7b4), C32e(0xacb915fa), C32e(0xf3fa0907), C32e(0xcfa06f25), - C32e(0xca20eaaf), C32e(0xf47d898e), C32e(0x476720e9), C32e(0x10382818), - C32e(0x6f0b64d5), C32e(0xf0738388), C32e(0x4afbb16f), C32e(0x5cca9672), - C32e(0x38546c24), C32e(0x575f08f1), C32e(0x732152c7), C32e(0x9764f351), - C32e(0xcbae6523), C32e(0xa125847c), C32e(0xe857bf9c), C32e(0x3e5d6321), - C32e(0x96ea7cdd), C32e(0x611e7fdc), C32e(0x0d9c9186), C32e(0x0f9b9485), - C32e(0xe04bab90), C32e(0x7cbac642), C32e(0x712657c4), C32e(0xcc29e5aa), - C32e(0x90e373d8), C32e(0x06090f05), C32e(0xf7f40301), C32e(0x1c2a3612), - C32e(0xc23cfea3), C32e(0x6a8be15f), C32e(0xaebe10f9), C32e(0x69026bd0), - C32e(0x17bfa891), C32e(0x9971e858), C32e(0x3a536927), C32e(0x27f7d0b9), - C32e(0xd9914838), C32e(0xebde3513), C32e(0x2be5ceb3), C32e(0x22775533), - C32e(0xd204d6bb), C32e(0xa9399070), C32e(0x07878089), C32e(0x33c1f2a7), - C32e(0x2decc1b6), C32e(0x3c5a6622), C32e(0x15b8ad92), C32e(0xc9a96020), - C32e(0x875cdb49), C32e(0xaab01aff), C32e(0x50d88878), C32e(0xa52b8e7a), - C32e(0x03898a8f), C32e(0x594a13f8), C32e(0x09929b80), C32e(0x1a233917), - C32e(0x651075da), C32e(0xd7845331), C32e(0x84d551c6), C32e(0xd003d3b8), - C32e(0x82dc5ec3), C32e(0x29e2cbb0), C32e(0x5ac39977), C32e(0x1e2d3311), - C32e(0x7b3d46cb), C32e(0xa8b71ffc), C32e(0x6d0c61d6), C32e(0x2c624e3a) -}; - -static const sph_u32 T0dn[] = { - C32e(0xf497a5c6), C32e(0x97eb84f8), C32e(0xb0c799ee), C32e(0x8cf78df6), - C32e(0x17e50dff), C32e(0xdcb7bdd6), C32e(0xc8a7b1de), C32e(0xfc395491), - C32e(0xf0c05060), C32e(0x05040302), C32e(0xe087a9ce), C32e(0x87ac7d56), - C32e(0x2bd519e7), C32e(0xa67162b5), C32e(0x319ae64d), C32e(0xb5c39aec), - C32e(0xcf05458f), C32e(0xbc3e9d1f), C32e(0xc0094089), C32e(0x92ef87fa), - C32e(0x3fc515ef), C32e(0x267febb2), C32e(0x4007c98e), C32e(0x1ded0bfb), - C32e(0x2f82ec41), C32e(0xa97d67b3), C32e(0x1cbefd5f), C32e(0x258aea45), - C32e(0xda46bf23), C32e(0x02a6f753), C32e(0xa1d396e4), C32e(0xed2d5b9b), - C32e(0x5deac275), C32e(0x24d91ce1), C32e(0xe97aae3d), C32e(0xbe986a4c), - C32e(0xeed85a6c), C32e(0xc3fc417e), C32e(0x06f102f5), C32e(0xd11d4f83), - C32e(0xe4d05c68), C32e(0x07a2f451), C32e(0x5cb934d1), C32e(0x18e908f9), - C32e(0xaedf93e2), C32e(0x954d73ab), C32e(0xf5c45362), C32e(0x41543f2a), - C32e(0x14100c08), C32e(0xf6315295), C32e(0xaf8c6546), C32e(0xe2215e9d), - C32e(0x78602830), C32e(0xf86ea137), C32e(0x11140f0a), C32e(0xc45eb52f), - C32e(0x1b1c090e), C32e(0x5a483624), C32e(0xb6369b1b), C32e(0x47a53ddf), - C32e(0x6a8126cd), C32e(0xbb9c694e), C32e(0x4cfecd7f), C32e(0xbacf9fea), - C32e(0x2d241b12), C32e(0xb93a9e1d), C32e(0x9cb07458), C32e(0x72682e34), - C32e(0x776c2d36), C32e(0xcda3b2dc), C32e(0x2973eeb4), C32e(0x16b6fb5b), - C32e(0x0153f6a4), C32e(0xd7ec4d76), C32e(0xa37561b7), C32e(0x49face7d), - C32e(0x8da47b52), C32e(0x42a13edd), C32e(0x93bc715e), C32e(0xa2269713), - C32e(0x0457f5a6), C32e(0xb86968b9), C32e(0x00000000), C32e(0x74992cc1), - C32e(0xa0806040), C32e(0x21dd1fe3), C32e(0x43f2c879), C32e(0x2c77edb6), - C32e(0xd9b3bed4), C32e(0xca01468d), C32e(0x70ced967), C32e(0xdde44b72), - C32e(0x7933de94), C32e(0x672bd498), C32e(0x237be8b0), C32e(0xde114a85), - C32e(0xbd6d6bbb), C32e(0x7e912ac5), C32e(0x349ee54f), C32e(0x3ac116ed), - C32e(0x5417c586), C32e(0x622fd79a), C32e(0xffcc5566), C32e(0xa7229411), - C32e(0x4a0fcf8a), C32e(0x30c910e9), C32e(0x0a080604), C32e(0x98e781fe), - C32e(0x0b5bf0a0), C32e(0xccf04478), C32e(0xd54aba25), C32e(0x3e96e34b), - C32e(0x0e5ff3a2), C32e(0x19bafe5d), C32e(0x5b1bc080), C32e(0x850a8a05), - C32e(0xec7ead3f), C32e(0xdf42bc21), C32e(0xd8e04870), C32e(0x0cf904f1), - C32e(0x7ac6df63), C32e(0x58eec177), C32e(0x9f4575af), C32e(0xa5846342), - C32e(0x50403020), C32e(0x2ed11ae5), C32e(0x12e10efd), C32e(0xb7656dbf), - C32e(0xd4194c81), C32e(0x3c301418), C32e(0x5f4c3526), C32e(0x719d2fc3), - C32e(0x3867e1be), C32e(0xfd6aa235), C32e(0x4f0bcc88), C32e(0x4b5c392e), - C32e(0xf93d5793), C32e(0x0daaf255), C32e(0x9de382fc), C32e(0xc9f4477a), - C32e(0xef8bacc8), C32e(0x326fe7ba), C32e(0x7d642b32), C32e(0xa4d795e6), - C32e(0xfb9ba0c0), C32e(0xb3329819), C32e(0x6827d19e), C32e(0x815d7fa3), - C32e(0xaa886644), C32e(0x82a87e54), C32e(0xe676ab3b), C32e(0x9e16830b), - C32e(0x4503ca8c), C32e(0x7b9529c7), C32e(0x6ed6d36b), C32e(0x44503c28), - C32e(0x8b5579a7), C32e(0x3d63e2bc), C32e(0x272c1d16), C32e(0x9a4176ad), - C32e(0x4dad3bdb), C32e(0xfac85664), C32e(0xd2e84e74), C32e(0x22281e14), - C32e(0x763fdb92), C32e(0x1e180a0c), C32e(0xb4906c48), C32e(0x376be4b8), - C32e(0xe7255d9f), C32e(0xb2616ebd), C32e(0x2a86ef43), C32e(0xf193a6c4), - C32e(0xe372a839), C32e(0xf762a431), C32e(0x59bd37d3), C32e(0x86ff8bf2), - C32e(0x56b132d5), C32e(0xc50d438b), C32e(0xebdc596e), C32e(0xc2afb7da), - C32e(0x8f028c01), C32e(0xac7964b1), C32e(0x6d23d29c), C32e(0x3b92e049), - C32e(0xc7abb4d8), C32e(0x1543faac), C32e(0x09fd07f3), C32e(0x6f8525cf), - C32e(0xea8fafca), C32e(0x89f38ef4), C32e(0x208ee947), C32e(0x28201810), - C32e(0x64ded56f), C32e(0x83fb88f0), C32e(0xb1946f4a), C32e(0x96b8725c), - C32e(0x6c702438), C32e(0x08aef157), C32e(0x52e6c773), C32e(0xf3355197), - C32e(0x658d23cb), C32e(0x84597ca1), C32e(0xbfcb9ce8), C32e(0x637c213e), - C32e(0x7c37dd96), C32e(0x7fc2dc61), C32e(0x911a860d), C32e(0x941e850f), - C32e(0xabdb90e0), C32e(0xc6f8427c), C32e(0x57e2c471), C32e(0xe583aacc), - C32e(0x733bd890), C32e(0x0f0c0506), C32e(0x03f501f7), C32e(0x3638121c), - C32e(0xfe9fa3c2), C32e(0xe1d45f6a), C32e(0x1047f9ae), C32e(0x6bd2d069), - C32e(0xa82e9117), C32e(0xe8295899), C32e(0x6974273a), C32e(0xd04eb927), - C32e(0x48a938d9), C32e(0x35cd13eb), C32e(0xce56b32b), C32e(0x55443322), - C32e(0xd6bfbbd2), C32e(0x904970a9), C32e(0x800e8907), C32e(0xf266a733), - C32e(0xc15ab62d), C32e(0x6678223c), C32e(0xad2a9215), C32e(0x608920c9), - C32e(0xdb154987), C32e(0x1a4fffaa), C32e(0x88a07850), C32e(0x8e517aa5), - C32e(0x8a068f03), C32e(0x13b2f859), C32e(0x9b128009), C32e(0x3934171a), - C32e(0x75cada65), C32e(0x53b531d7), C32e(0x5113c684), C32e(0xd3bbb8d0), - C32e(0x5e1fc382), C32e(0xcb52b029), C32e(0x99b4775a), C32e(0x333c111e), - C32e(0x46f6cb7b), C32e(0x1f4bfca8), C32e(0x61dad66d), C32e(0x4e583a2c) -}; - -static const sph_u32 T1up[] = { - C32e(0xc6c632f4), C32e(0xf8f86f97), C32e(0xeeee5eb0), C32e(0xf6f67a8c), - C32e(0xffffe817), C32e(0xd6d60adc), C32e(0xdede16c8), C32e(0x91916dfc), - C32e(0x606090f0), C32e(0x02020705), C32e(0xcece2ee0), C32e(0x5656d187), - C32e(0xe7e7cc2b), C32e(0xb5b513a6), C32e(0x4d4d7c31), C32e(0xecec59b5), - C32e(0x8f8f40cf), C32e(0x1f1fa3bc), C32e(0x898949c0), C32e(0xfafa6892), - C32e(0xefefd03f), C32e(0xb2b29426), C32e(0x8e8ece40), C32e(0xfbfbe61d), - C32e(0x41416e2f), C32e(0xb3b31aa9), C32e(0x5f5f431c), C32e(0x45456025), - C32e(0x2323f9da), C32e(0x53535102), C32e(0xe4e445a1), C32e(0x9b9b76ed), - C32e(0x7575285d), C32e(0xe1e1c524), C32e(0x3d3dd4e9), C32e(0x4c4cf2be), - C32e(0x6c6c82ee), C32e(0x7e7ebdc3), C32e(0xf5f5f306), C32e(0x838352d1), - C32e(0x68688ce4), C32e(0x51515607), C32e(0xd1d18d5c), C32e(0xf9f9e118), - C32e(0xe2e24cae), C32e(0xabab3e95), C32e(0x626297f5), C32e(0x2a2a6b41), - C32e(0x08081c14), C32e(0x959563f6), C32e(0x4646e9af), C32e(0x9d9d7fe2), - C32e(0x30304878), C32e(0x3737cff8), C32e(0x0a0a1b11), C32e(0x2f2febc4), - C32e(0x0e0e151b), C32e(0x24247e5a), C32e(0x1b1badb6), C32e(0xdfdf9847), - C32e(0xcdcda76a), C32e(0x4e4ef5bb), C32e(0x7f7f334c), C32e(0xeaea50ba), - C32e(0x12123f2d), C32e(0x1d1da4b9), C32e(0x5858c49c), C32e(0x34344672), - C32e(0x36364177), C32e(0xdcdc11cd), C32e(0xb4b49d29), C32e(0x5b5b4d16), - C32e(0xa4a4a501), C32e(0x7676a1d7), C32e(0xb7b714a3), C32e(0x7d7d3449), - C32e(0x5252df8d), C32e(0xdddd9f42), C32e(0x5e5ecd93), C32e(0x1313b1a2), - C32e(0xa6a6a204), C32e(0xb9b901b8), C32e(0x00000000), C32e(0xc1c1b574), - C32e(0x4040e0a0), C32e(0xe3e3c221), C32e(0x79793a43), C32e(0xb6b69a2c), - C32e(0xd4d40dd9), C32e(0x8d8d47ca), C32e(0x67671770), C32e(0x7272afdd), - C32e(0x9494ed79), C32e(0x9898ff67), C32e(0xb0b09323), C32e(0x85855bde), - C32e(0xbbbb06bd), C32e(0xc5c5bb7e), C32e(0x4f4f7b34), C32e(0xededd73a), - C32e(0x8686d254), C32e(0x9a9af862), C32e(0x666699ff), C32e(0x1111b6a7), - C32e(0x8a8ac04a), C32e(0xe9e9d930), C32e(0x04040e0a), C32e(0xfefe6698), - C32e(0xa0a0ab0b), C32e(0x7878b4cc), C32e(0x2525f0d5), C32e(0x4b4b753e), - C32e(0xa2a2ac0e), C32e(0x5d5d4419), C32e(0x8080db5b), C32e(0x05058085), - C32e(0x3f3fd3ec), C32e(0x2121fedf), C32e(0x7070a8d8), C32e(0xf1f1fd0c), - C32e(0x6363197a), C32e(0x77772f58), C32e(0xafaf309f), C32e(0x4242e7a5), - C32e(0x20207050), C32e(0xe5e5cb2e), C32e(0xfdfdef12), C32e(0xbfbf08b7), - C32e(0x818155d4), C32e(0x1818243c), C32e(0x2626795f), C32e(0xc3c3b271), - C32e(0xbebe8638), C32e(0x3535c8fd), C32e(0x8888c74f), C32e(0x2e2e654b), - C32e(0x93936af9), C32e(0x5555580d), C32e(0xfcfc619d), C32e(0x7a7ab3c9), - C32e(0xc8c827ef), C32e(0xbaba8832), C32e(0x32324f7d), C32e(0xe6e642a4), - C32e(0xc0c03bfb), C32e(0x1919aab3), C32e(0x9e9ef668), C32e(0xa3a32281), - C32e(0x4444eeaa), C32e(0x5454d682), C32e(0x3b3bdde6), C32e(0x0b0b959e), - C32e(0x8c8cc945), C32e(0xc7c7bc7b), C32e(0x6b6b056e), C32e(0x28286c44), - C32e(0xa7a72c8b), C32e(0xbcbc813d), C32e(0x16163127), C32e(0xadad379a), - C32e(0xdbdb964d), C32e(0x64649efa), C32e(0x7474a6d2), C32e(0x14143622), - C32e(0x9292e476), C32e(0x0c0c121e), C32e(0x4848fcb4), C32e(0xb8b88f37), - C32e(0x9f9f78e7), C32e(0xbdbd0fb2), C32e(0x4343692a), C32e(0xc4c435f1), - C32e(0x3939dae3), C32e(0x3131c6f7), C32e(0xd3d38a59), C32e(0xf2f27486), - C32e(0xd5d58356), C32e(0x8b8b4ec5), C32e(0x6e6e85eb), C32e(0xdada18c2), - C32e(0x01018e8f), C32e(0xb1b11dac), C32e(0x9c9cf16d), C32e(0x4949723b), - C32e(0xd8d81fc7), C32e(0xacacb915), C32e(0xf3f3fa09), C32e(0xcfcfa06f), - C32e(0xcaca20ea), C32e(0xf4f47d89), C32e(0x47476720), C32e(0x10103828), - C32e(0x6f6f0b64), C32e(0xf0f07383), C32e(0x4a4afbb1), C32e(0x5c5cca96), - C32e(0x3838546c), C32e(0x57575f08), C32e(0x73732152), C32e(0x979764f3), - C32e(0xcbcbae65), C32e(0xa1a12584), C32e(0xe8e857bf), C32e(0x3e3e5d63), - C32e(0x9696ea7c), C32e(0x61611e7f), C32e(0x0d0d9c91), C32e(0x0f0f9b94), - C32e(0xe0e04bab), C32e(0x7c7cbac6), C32e(0x71712657), C32e(0xcccc29e5), - C32e(0x9090e373), C32e(0x0606090f), C32e(0xf7f7f403), C32e(0x1c1c2a36), - C32e(0xc2c23cfe), C32e(0x6a6a8be1), C32e(0xaeaebe10), C32e(0x6969026b), - C32e(0x1717bfa8), C32e(0x999971e8), C32e(0x3a3a5369), C32e(0x2727f7d0), - C32e(0xd9d99148), C32e(0xebebde35), C32e(0x2b2be5ce), C32e(0x22227755), - C32e(0xd2d204d6), C32e(0xa9a93990), C32e(0x07078780), C32e(0x3333c1f2), - C32e(0x2d2decc1), C32e(0x3c3c5a66), C32e(0x1515b8ad), C32e(0xc9c9a960), - C32e(0x87875cdb), C32e(0xaaaab01a), C32e(0x5050d888), C32e(0xa5a52b8e), - C32e(0x0303898a), C32e(0x59594a13), C32e(0x0909929b), C32e(0x1a1a2339), - C32e(0x65651075), C32e(0xd7d78453), C32e(0x8484d551), C32e(0xd0d003d3), - C32e(0x8282dc5e), C32e(0x2929e2cb), C32e(0x5a5ac399), C32e(0x1e1e2d33), - C32e(0x7b7b3d46), C32e(0xa8a8b71f), C32e(0x6d6d0c61), C32e(0x2c2c624e) -}; - -static const sph_u32 T1dn[] = { - C32e(0xa5f497a5), C32e(0x8497eb84), C32e(0x99b0c799), C32e(0x8d8cf78d), - C32e(0x0d17e50d), C32e(0xbddcb7bd), C32e(0xb1c8a7b1), C32e(0x54fc3954), - C32e(0x50f0c050), C32e(0x03050403), C32e(0xa9e087a9), C32e(0x7d87ac7d), - C32e(0x192bd519), C32e(0x62a67162), C32e(0xe6319ae6), C32e(0x9ab5c39a), - C32e(0x45cf0545), C32e(0x9dbc3e9d), C32e(0x40c00940), C32e(0x8792ef87), - C32e(0x153fc515), C32e(0xeb267feb), C32e(0xc94007c9), C32e(0x0b1ded0b), - C32e(0xec2f82ec), C32e(0x67a97d67), C32e(0xfd1cbefd), C32e(0xea258aea), - C32e(0xbfda46bf), C32e(0xf702a6f7), C32e(0x96a1d396), C32e(0x5bed2d5b), - C32e(0xc25deac2), C32e(0x1c24d91c), C32e(0xaee97aae), C32e(0x6abe986a), - C32e(0x5aeed85a), C32e(0x41c3fc41), C32e(0x0206f102), C32e(0x4fd11d4f), - C32e(0x5ce4d05c), C32e(0xf407a2f4), C32e(0x345cb934), C32e(0x0818e908), - C32e(0x93aedf93), C32e(0x73954d73), C32e(0x53f5c453), C32e(0x3f41543f), - C32e(0x0c14100c), C32e(0x52f63152), C32e(0x65af8c65), C32e(0x5ee2215e), - C32e(0x28786028), C32e(0xa1f86ea1), C32e(0x0f11140f), C32e(0xb5c45eb5), - C32e(0x091b1c09), C32e(0x365a4836), C32e(0x9bb6369b), C32e(0x3d47a53d), - C32e(0x266a8126), C32e(0x69bb9c69), C32e(0xcd4cfecd), C32e(0x9fbacf9f), - C32e(0x1b2d241b), C32e(0x9eb93a9e), C32e(0x749cb074), C32e(0x2e72682e), - C32e(0x2d776c2d), C32e(0xb2cda3b2), C32e(0xee2973ee), C32e(0xfb16b6fb), - C32e(0xf60153f6), C32e(0x4dd7ec4d), C32e(0x61a37561), C32e(0xce49face), - C32e(0x7b8da47b), C32e(0x3e42a13e), C32e(0x7193bc71), C32e(0x97a22697), - C32e(0xf50457f5), C32e(0x68b86968), C32e(0x00000000), C32e(0x2c74992c), - C32e(0x60a08060), C32e(0x1f21dd1f), C32e(0xc843f2c8), C32e(0xed2c77ed), - C32e(0xbed9b3be), C32e(0x46ca0146), C32e(0xd970ced9), C32e(0x4bdde44b), - C32e(0xde7933de), C32e(0xd4672bd4), C32e(0xe8237be8), C32e(0x4ade114a), - C32e(0x6bbd6d6b), C32e(0x2a7e912a), C32e(0xe5349ee5), C32e(0x163ac116), - C32e(0xc55417c5), C32e(0xd7622fd7), C32e(0x55ffcc55), C32e(0x94a72294), - C32e(0xcf4a0fcf), C32e(0x1030c910), C32e(0x060a0806), C32e(0x8198e781), - C32e(0xf00b5bf0), C32e(0x44ccf044), C32e(0xbad54aba), C32e(0xe33e96e3), - C32e(0xf30e5ff3), C32e(0xfe19bafe), C32e(0xc05b1bc0), C32e(0x8a850a8a), - C32e(0xadec7ead), C32e(0xbcdf42bc), C32e(0x48d8e048), C32e(0x040cf904), - C32e(0xdf7ac6df), C32e(0xc158eec1), C32e(0x759f4575), C32e(0x63a58463), - C32e(0x30504030), C32e(0x1a2ed11a), C32e(0x0e12e10e), C32e(0x6db7656d), - C32e(0x4cd4194c), C32e(0x143c3014), C32e(0x355f4c35), C32e(0x2f719d2f), - C32e(0xe13867e1), C32e(0xa2fd6aa2), C32e(0xcc4f0bcc), C32e(0x394b5c39), - C32e(0x57f93d57), C32e(0xf20daaf2), C32e(0x829de382), C32e(0x47c9f447), - C32e(0xacef8bac), C32e(0xe7326fe7), C32e(0x2b7d642b), C32e(0x95a4d795), - C32e(0xa0fb9ba0), C32e(0x98b33298), C32e(0xd16827d1), C32e(0x7f815d7f), - C32e(0x66aa8866), C32e(0x7e82a87e), C32e(0xabe676ab), C32e(0x839e1683), - C32e(0xca4503ca), C32e(0x297b9529), C32e(0xd36ed6d3), C32e(0x3c44503c), - C32e(0x798b5579), C32e(0xe23d63e2), C32e(0x1d272c1d), C32e(0x769a4176), - C32e(0x3b4dad3b), C32e(0x56fac856), C32e(0x4ed2e84e), C32e(0x1e22281e), - C32e(0xdb763fdb), C32e(0x0a1e180a), C32e(0x6cb4906c), C32e(0xe4376be4), - C32e(0x5de7255d), C32e(0x6eb2616e), C32e(0xef2a86ef), C32e(0xa6f193a6), - C32e(0xa8e372a8), C32e(0xa4f762a4), C32e(0x3759bd37), C32e(0x8b86ff8b), - C32e(0x3256b132), C32e(0x43c50d43), C32e(0x59ebdc59), C32e(0xb7c2afb7), - C32e(0x8c8f028c), C32e(0x64ac7964), C32e(0xd26d23d2), C32e(0xe03b92e0), - C32e(0xb4c7abb4), C32e(0xfa1543fa), C32e(0x0709fd07), C32e(0x256f8525), - C32e(0xafea8faf), C32e(0x8e89f38e), C32e(0xe9208ee9), C32e(0x18282018), - C32e(0xd564ded5), C32e(0x8883fb88), C32e(0x6fb1946f), C32e(0x7296b872), - C32e(0x246c7024), C32e(0xf108aef1), C32e(0xc752e6c7), C32e(0x51f33551), - C32e(0x23658d23), C32e(0x7c84597c), C32e(0x9cbfcb9c), C32e(0x21637c21), - C32e(0xdd7c37dd), C32e(0xdc7fc2dc), C32e(0x86911a86), C32e(0x85941e85), - C32e(0x90abdb90), C32e(0x42c6f842), C32e(0xc457e2c4), C32e(0xaae583aa), - C32e(0xd8733bd8), C32e(0x050f0c05), C32e(0x0103f501), C32e(0x12363812), - C32e(0xa3fe9fa3), C32e(0x5fe1d45f), C32e(0xf91047f9), C32e(0xd06bd2d0), - C32e(0x91a82e91), C32e(0x58e82958), C32e(0x27697427), C32e(0xb9d04eb9), - C32e(0x3848a938), C32e(0x1335cd13), C32e(0xb3ce56b3), C32e(0x33554433), - C32e(0xbbd6bfbb), C32e(0x70904970), C32e(0x89800e89), C32e(0xa7f266a7), - C32e(0xb6c15ab6), C32e(0x22667822), C32e(0x92ad2a92), C32e(0x20608920), - C32e(0x49db1549), C32e(0xff1a4fff), C32e(0x7888a078), C32e(0x7a8e517a), - C32e(0x8f8a068f), C32e(0xf813b2f8), C32e(0x809b1280), C32e(0x17393417), - C32e(0xda75cada), C32e(0x3153b531), C32e(0xc65113c6), C32e(0xb8d3bbb8), - C32e(0xc35e1fc3), C32e(0xb0cb52b0), C32e(0x7799b477), C32e(0x11333c11), - C32e(0xcb46f6cb), C32e(0xfc1f4bfc), C32e(0xd661dad6), C32e(0x3a4e583a) -}; - -static const sph_u32 T2up[] = { - C32e(0xa5c6c632), C32e(0x84f8f86f), C32e(0x99eeee5e), C32e(0x8df6f67a), - C32e(0x0dffffe8), C32e(0xbdd6d60a), C32e(0xb1dede16), C32e(0x5491916d), - C32e(0x50606090), C32e(0x03020207), C32e(0xa9cece2e), C32e(0x7d5656d1), - C32e(0x19e7e7cc), C32e(0x62b5b513), C32e(0xe64d4d7c), C32e(0x9aecec59), - C32e(0x458f8f40), C32e(0x9d1f1fa3), C32e(0x40898949), C32e(0x87fafa68), - C32e(0x15efefd0), C32e(0xebb2b294), C32e(0xc98e8ece), C32e(0x0bfbfbe6), - C32e(0xec41416e), C32e(0x67b3b31a), C32e(0xfd5f5f43), C32e(0xea454560), - C32e(0xbf2323f9), C32e(0xf7535351), C32e(0x96e4e445), C32e(0x5b9b9b76), - C32e(0xc2757528), C32e(0x1ce1e1c5), C32e(0xae3d3dd4), C32e(0x6a4c4cf2), - C32e(0x5a6c6c82), C32e(0x417e7ebd), C32e(0x02f5f5f3), C32e(0x4f838352), - C32e(0x5c68688c), C32e(0xf4515156), C32e(0x34d1d18d), C32e(0x08f9f9e1), - C32e(0x93e2e24c), C32e(0x73abab3e), C32e(0x53626297), C32e(0x3f2a2a6b), - C32e(0x0c08081c), C32e(0x52959563), C32e(0x654646e9), C32e(0x5e9d9d7f), - C32e(0x28303048), C32e(0xa13737cf), C32e(0x0f0a0a1b), C32e(0xb52f2feb), - C32e(0x090e0e15), C32e(0x3624247e), C32e(0x9b1b1bad), C32e(0x3ddfdf98), - C32e(0x26cdcda7), C32e(0x694e4ef5), C32e(0xcd7f7f33), C32e(0x9feaea50), - C32e(0x1b12123f), C32e(0x9e1d1da4), C32e(0x745858c4), C32e(0x2e343446), - C32e(0x2d363641), C32e(0xb2dcdc11), C32e(0xeeb4b49d), C32e(0xfb5b5b4d), - C32e(0xf6a4a4a5), C32e(0x4d7676a1), C32e(0x61b7b714), C32e(0xce7d7d34), - C32e(0x7b5252df), C32e(0x3edddd9f), C32e(0x715e5ecd), C32e(0x971313b1), - C32e(0xf5a6a6a2), C32e(0x68b9b901), C32e(0x00000000), C32e(0x2cc1c1b5), - C32e(0x604040e0), C32e(0x1fe3e3c2), C32e(0xc879793a), C32e(0xedb6b69a), - C32e(0xbed4d40d), C32e(0x468d8d47), C32e(0xd9676717), C32e(0x4b7272af), - C32e(0xde9494ed), C32e(0xd49898ff), C32e(0xe8b0b093), C32e(0x4a85855b), - C32e(0x6bbbbb06), C32e(0x2ac5c5bb), C32e(0xe54f4f7b), C32e(0x16ededd7), - C32e(0xc58686d2), C32e(0xd79a9af8), C32e(0x55666699), C32e(0x941111b6), - C32e(0xcf8a8ac0), C32e(0x10e9e9d9), C32e(0x0604040e), C32e(0x81fefe66), - C32e(0xf0a0a0ab), C32e(0x447878b4), C32e(0xba2525f0), C32e(0xe34b4b75), - C32e(0xf3a2a2ac), C32e(0xfe5d5d44), C32e(0xc08080db), C32e(0x8a050580), - C32e(0xad3f3fd3), C32e(0xbc2121fe), C32e(0x487070a8), C32e(0x04f1f1fd), - C32e(0xdf636319), C32e(0xc177772f), C32e(0x75afaf30), C32e(0x634242e7), - C32e(0x30202070), C32e(0x1ae5e5cb), C32e(0x0efdfdef), C32e(0x6dbfbf08), - C32e(0x4c818155), C32e(0x14181824), C32e(0x35262679), C32e(0x2fc3c3b2), - C32e(0xe1bebe86), C32e(0xa23535c8), C32e(0xcc8888c7), C32e(0x392e2e65), - C32e(0x5793936a), C32e(0xf2555558), C32e(0x82fcfc61), C32e(0x477a7ab3), - C32e(0xacc8c827), C32e(0xe7baba88), C32e(0x2b32324f), C32e(0x95e6e642), - C32e(0xa0c0c03b), C32e(0x981919aa), C32e(0xd19e9ef6), C32e(0x7fa3a322), - C32e(0x664444ee), C32e(0x7e5454d6), C32e(0xab3b3bdd), C32e(0x830b0b95), - C32e(0xca8c8cc9), C32e(0x29c7c7bc), C32e(0xd36b6b05), C32e(0x3c28286c), - C32e(0x79a7a72c), C32e(0xe2bcbc81), C32e(0x1d161631), C32e(0x76adad37), - C32e(0x3bdbdb96), C32e(0x5664649e), C32e(0x4e7474a6), C32e(0x1e141436), - C32e(0xdb9292e4), C32e(0x0a0c0c12), C32e(0x6c4848fc), C32e(0xe4b8b88f), - C32e(0x5d9f9f78), C32e(0x6ebdbd0f), C32e(0xef434369), C32e(0xa6c4c435), - C32e(0xa83939da), C32e(0xa43131c6), C32e(0x37d3d38a), C32e(0x8bf2f274), - C32e(0x32d5d583), C32e(0x438b8b4e), C32e(0x596e6e85), C32e(0xb7dada18), - C32e(0x8c01018e), C32e(0x64b1b11d), C32e(0xd29c9cf1), C32e(0xe0494972), - C32e(0xb4d8d81f), C32e(0xfaacacb9), C32e(0x07f3f3fa), C32e(0x25cfcfa0), - C32e(0xafcaca20), C32e(0x8ef4f47d), C32e(0xe9474767), C32e(0x18101038), - C32e(0xd56f6f0b), C32e(0x88f0f073), C32e(0x6f4a4afb), C32e(0x725c5cca), - C32e(0x24383854), C32e(0xf157575f), C32e(0xc7737321), C32e(0x51979764), - C32e(0x23cbcbae), C32e(0x7ca1a125), C32e(0x9ce8e857), C32e(0x213e3e5d), - C32e(0xdd9696ea), C32e(0xdc61611e), C32e(0x860d0d9c), C32e(0x850f0f9b), - C32e(0x90e0e04b), C32e(0x427c7cba), C32e(0xc4717126), C32e(0xaacccc29), - C32e(0xd89090e3), C32e(0x05060609), C32e(0x01f7f7f4), C32e(0x121c1c2a), - C32e(0xa3c2c23c), C32e(0x5f6a6a8b), C32e(0xf9aeaebe), C32e(0xd0696902), - C32e(0x911717bf), C32e(0x58999971), C32e(0x273a3a53), C32e(0xb92727f7), - C32e(0x38d9d991), C32e(0x13ebebde), C32e(0xb32b2be5), C32e(0x33222277), - C32e(0xbbd2d204), C32e(0x70a9a939), C32e(0x89070787), C32e(0xa73333c1), - C32e(0xb62d2dec), C32e(0x223c3c5a), C32e(0x921515b8), C32e(0x20c9c9a9), - C32e(0x4987875c), C32e(0xffaaaab0), C32e(0x785050d8), C32e(0x7aa5a52b), - C32e(0x8f030389), C32e(0xf859594a), C32e(0x80090992), C32e(0x171a1a23), - C32e(0xda656510), C32e(0x31d7d784), C32e(0xc68484d5), C32e(0xb8d0d003), - C32e(0xc38282dc), C32e(0xb02929e2), C32e(0x775a5ac3), C32e(0x111e1e2d), - C32e(0xcb7b7b3d), C32e(0xfca8a8b7), C32e(0xd66d6d0c), C32e(0x3a2c2c62) -}; - -static const sph_u32 T2dn[] = { - C32e(0xf4a5f497), C32e(0x978497eb), C32e(0xb099b0c7), C32e(0x8c8d8cf7), - C32e(0x170d17e5), C32e(0xdcbddcb7), C32e(0xc8b1c8a7), C32e(0xfc54fc39), - C32e(0xf050f0c0), C32e(0x05030504), C32e(0xe0a9e087), C32e(0x877d87ac), - C32e(0x2b192bd5), C32e(0xa662a671), C32e(0x31e6319a), C32e(0xb59ab5c3), - C32e(0xcf45cf05), C32e(0xbc9dbc3e), C32e(0xc040c009), C32e(0x928792ef), - C32e(0x3f153fc5), C32e(0x26eb267f), C32e(0x40c94007), C32e(0x1d0b1ded), - C32e(0x2fec2f82), C32e(0xa967a97d), C32e(0x1cfd1cbe), C32e(0x25ea258a), - C32e(0xdabfda46), C32e(0x02f702a6), C32e(0xa196a1d3), C32e(0xed5bed2d), - C32e(0x5dc25dea), C32e(0x241c24d9), C32e(0xe9aee97a), C32e(0xbe6abe98), - C32e(0xee5aeed8), C32e(0xc341c3fc), C32e(0x060206f1), C32e(0xd14fd11d), - C32e(0xe45ce4d0), C32e(0x07f407a2), C32e(0x5c345cb9), C32e(0x180818e9), - C32e(0xae93aedf), C32e(0x9573954d), C32e(0xf553f5c4), C32e(0x413f4154), - C32e(0x140c1410), C32e(0xf652f631), C32e(0xaf65af8c), C32e(0xe25ee221), - C32e(0x78287860), C32e(0xf8a1f86e), C32e(0x110f1114), C32e(0xc4b5c45e), - C32e(0x1b091b1c), C32e(0x5a365a48), C32e(0xb69bb636), C32e(0x473d47a5), - C32e(0x6a266a81), C32e(0xbb69bb9c), C32e(0x4ccd4cfe), C32e(0xba9fbacf), - C32e(0x2d1b2d24), C32e(0xb99eb93a), C32e(0x9c749cb0), C32e(0x722e7268), - C32e(0x772d776c), C32e(0xcdb2cda3), C32e(0x29ee2973), C32e(0x16fb16b6), - C32e(0x01f60153), C32e(0xd74dd7ec), C32e(0xa361a375), C32e(0x49ce49fa), - C32e(0x8d7b8da4), C32e(0x423e42a1), C32e(0x937193bc), C32e(0xa297a226), - C32e(0x04f50457), C32e(0xb868b869), C32e(0x00000000), C32e(0x742c7499), - C32e(0xa060a080), C32e(0x211f21dd), C32e(0x43c843f2), C32e(0x2ced2c77), - C32e(0xd9bed9b3), C32e(0xca46ca01), C32e(0x70d970ce), C32e(0xdd4bdde4), - C32e(0x79de7933), C32e(0x67d4672b), C32e(0x23e8237b), C32e(0xde4ade11), - C32e(0xbd6bbd6d), C32e(0x7e2a7e91), C32e(0x34e5349e), C32e(0x3a163ac1), - C32e(0x54c55417), C32e(0x62d7622f), C32e(0xff55ffcc), C32e(0xa794a722), - C32e(0x4acf4a0f), C32e(0x301030c9), C32e(0x0a060a08), C32e(0x988198e7), - C32e(0x0bf00b5b), C32e(0xcc44ccf0), C32e(0xd5bad54a), C32e(0x3ee33e96), - C32e(0x0ef30e5f), C32e(0x19fe19ba), C32e(0x5bc05b1b), C32e(0x858a850a), - C32e(0xecadec7e), C32e(0xdfbcdf42), C32e(0xd848d8e0), C32e(0x0c040cf9), - C32e(0x7adf7ac6), C32e(0x58c158ee), C32e(0x9f759f45), C32e(0xa563a584), - C32e(0x50305040), C32e(0x2e1a2ed1), C32e(0x120e12e1), C32e(0xb76db765), - C32e(0xd44cd419), C32e(0x3c143c30), C32e(0x5f355f4c), C32e(0x712f719d), - C32e(0x38e13867), C32e(0xfda2fd6a), C32e(0x4fcc4f0b), C32e(0x4b394b5c), - C32e(0xf957f93d), C32e(0x0df20daa), C32e(0x9d829de3), C32e(0xc947c9f4), - C32e(0xefacef8b), C32e(0x32e7326f), C32e(0x7d2b7d64), C32e(0xa495a4d7), - C32e(0xfba0fb9b), C32e(0xb398b332), C32e(0x68d16827), C32e(0x817f815d), - C32e(0xaa66aa88), C32e(0x827e82a8), C32e(0xe6abe676), C32e(0x9e839e16), - C32e(0x45ca4503), C32e(0x7b297b95), C32e(0x6ed36ed6), C32e(0x443c4450), - C32e(0x8b798b55), C32e(0x3de23d63), C32e(0x271d272c), C32e(0x9a769a41), - C32e(0x4d3b4dad), C32e(0xfa56fac8), C32e(0xd24ed2e8), C32e(0x221e2228), - C32e(0x76db763f), C32e(0x1e0a1e18), C32e(0xb46cb490), C32e(0x37e4376b), - C32e(0xe75de725), C32e(0xb26eb261), C32e(0x2aef2a86), C32e(0xf1a6f193), - C32e(0xe3a8e372), C32e(0xf7a4f762), C32e(0x593759bd), C32e(0x868b86ff), - C32e(0x563256b1), C32e(0xc543c50d), C32e(0xeb59ebdc), C32e(0xc2b7c2af), - C32e(0x8f8c8f02), C32e(0xac64ac79), C32e(0x6dd26d23), C32e(0x3be03b92), - C32e(0xc7b4c7ab), C32e(0x15fa1543), C32e(0x090709fd), C32e(0x6f256f85), - C32e(0xeaafea8f), C32e(0x898e89f3), C32e(0x20e9208e), C32e(0x28182820), - C32e(0x64d564de), C32e(0x838883fb), C32e(0xb16fb194), C32e(0x967296b8), - C32e(0x6c246c70), C32e(0x08f108ae), C32e(0x52c752e6), C32e(0xf351f335), - C32e(0x6523658d), C32e(0x847c8459), C32e(0xbf9cbfcb), C32e(0x6321637c), - C32e(0x7cdd7c37), C32e(0x7fdc7fc2), C32e(0x9186911a), C32e(0x9485941e), - C32e(0xab90abdb), C32e(0xc642c6f8), C32e(0x57c457e2), C32e(0xe5aae583), - C32e(0x73d8733b), C32e(0x0f050f0c), C32e(0x030103f5), C32e(0x36123638), - C32e(0xfea3fe9f), C32e(0xe15fe1d4), C32e(0x10f91047), C32e(0x6bd06bd2), - C32e(0xa891a82e), C32e(0xe858e829), C32e(0x69276974), C32e(0xd0b9d04e), - C32e(0x483848a9), C32e(0x351335cd), C32e(0xceb3ce56), C32e(0x55335544), - C32e(0xd6bbd6bf), C32e(0x90709049), C32e(0x8089800e), C32e(0xf2a7f266), - C32e(0xc1b6c15a), C32e(0x66226678), C32e(0xad92ad2a), C32e(0x60206089), - C32e(0xdb49db15), C32e(0x1aff1a4f), C32e(0x887888a0), C32e(0x8e7a8e51), - C32e(0x8a8f8a06), C32e(0x13f813b2), C32e(0x9b809b12), C32e(0x39173934), - C32e(0x75da75ca), C32e(0x533153b5), C32e(0x51c65113), C32e(0xd3b8d3bb), - C32e(0x5ec35e1f), C32e(0xcbb0cb52), C32e(0x997799b4), C32e(0x3311333c), - C32e(0x46cb46f6), C32e(0x1ffc1f4b), C32e(0x61d661da), C32e(0x4e3a4e58) -}; - -static const sph_u32 T3up[] = { - C32e(0x97a5c6c6), C32e(0xeb84f8f8), C32e(0xc799eeee), C32e(0xf78df6f6), - C32e(0xe50dffff), C32e(0xb7bdd6d6), C32e(0xa7b1dede), C32e(0x39549191), - C32e(0xc0506060), C32e(0x04030202), C32e(0x87a9cece), C32e(0xac7d5656), - C32e(0xd519e7e7), C32e(0x7162b5b5), C32e(0x9ae64d4d), C32e(0xc39aecec), - C32e(0x05458f8f), C32e(0x3e9d1f1f), C32e(0x09408989), C32e(0xef87fafa), - C32e(0xc515efef), C32e(0x7febb2b2), C32e(0x07c98e8e), C32e(0xed0bfbfb), - C32e(0x82ec4141), C32e(0x7d67b3b3), C32e(0xbefd5f5f), C32e(0x8aea4545), - C32e(0x46bf2323), C32e(0xa6f75353), C32e(0xd396e4e4), C32e(0x2d5b9b9b), - C32e(0xeac27575), C32e(0xd91ce1e1), C32e(0x7aae3d3d), C32e(0x986a4c4c), - C32e(0xd85a6c6c), C32e(0xfc417e7e), C32e(0xf102f5f5), C32e(0x1d4f8383), - C32e(0xd05c6868), C32e(0xa2f45151), C32e(0xb934d1d1), C32e(0xe908f9f9), - C32e(0xdf93e2e2), C32e(0x4d73abab), C32e(0xc4536262), C32e(0x543f2a2a), - C32e(0x100c0808), C32e(0x31529595), C32e(0x8c654646), C32e(0x215e9d9d), - C32e(0x60283030), C32e(0x6ea13737), C32e(0x140f0a0a), C32e(0x5eb52f2f), - C32e(0x1c090e0e), C32e(0x48362424), C32e(0x369b1b1b), C32e(0xa53ddfdf), - C32e(0x8126cdcd), C32e(0x9c694e4e), C32e(0xfecd7f7f), C32e(0xcf9feaea), - C32e(0x241b1212), C32e(0x3a9e1d1d), C32e(0xb0745858), C32e(0x682e3434), - C32e(0x6c2d3636), C32e(0xa3b2dcdc), C32e(0x73eeb4b4), C32e(0xb6fb5b5b), - C32e(0x53f6a4a4), C32e(0xec4d7676), C32e(0x7561b7b7), C32e(0xface7d7d), - C32e(0xa47b5252), C32e(0xa13edddd), C32e(0xbc715e5e), C32e(0x26971313), - C32e(0x57f5a6a6), C32e(0x6968b9b9), C32e(0x00000000), C32e(0x992cc1c1), - C32e(0x80604040), C32e(0xdd1fe3e3), C32e(0xf2c87979), C32e(0x77edb6b6), - C32e(0xb3bed4d4), C32e(0x01468d8d), C32e(0xced96767), C32e(0xe44b7272), - C32e(0x33de9494), C32e(0x2bd49898), C32e(0x7be8b0b0), C32e(0x114a8585), - C32e(0x6d6bbbbb), C32e(0x912ac5c5), C32e(0x9ee54f4f), C32e(0xc116eded), - C32e(0x17c58686), C32e(0x2fd79a9a), C32e(0xcc556666), C32e(0x22941111), - C32e(0x0fcf8a8a), C32e(0xc910e9e9), C32e(0x08060404), C32e(0xe781fefe), - C32e(0x5bf0a0a0), C32e(0xf0447878), C32e(0x4aba2525), C32e(0x96e34b4b), - C32e(0x5ff3a2a2), C32e(0xbafe5d5d), C32e(0x1bc08080), C32e(0x0a8a0505), - C32e(0x7ead3f3f), C32e(0x42bc2121), C32e(0xe0487070), C32e(0xf904f1f1), - C32e(0xc6df6363), C32e(0xeec17777), C32e(0x4575afaf), C32e(0x84634242), - C32e(0x40302020), C32e(0xd11ae5e5), C32e(0xe10efdfd), C32e(0x656dbfbf), - C32e(0x194c8181), C32e(0x30141818), C32e(0x4c352626), C32e(0x9d2fc3c3), - C32e(0x67e1bebe), C32e(0x6aa23535), C32e(0x0bcc8888), C32e(0x5c392e2e), - C32e(0x3d579393), C32e(0xaaf25555), C32e(0xe382fcfc), C32e(0xf4477a7a), - C32e(0x8bacc8c8), C32e(0x6fe7baba), C32e(0x642b3232), C32e(0xd795e6e6), - C32e(0x9ba0c0c0), C32e(0x32981919), C32e(0x27d19e9e), C32e(0x5d7fa3a3), - C32e(0x88664444), C32e(0xa87e5454), C32e(0x76ab3b3b), C32e(0x16830b0b), - C32e(0x03ca8c8c), C32e(0x9529c7c7), C32e(0xd6d36b6b), C32e(0x503c2828), - C32e(0x5579a7a7), C32e(0x63e2bcbc), C32e(0x2c1d1616), C32e(0x4176adad), - C32e(0xad3bdbdb), C32e(0xc8566464), C32e(0xe84e7474), C32e(0x281e1414), - C32e(0x3fdb9292), C32e(0x180a0c0c), C32e(0x906c4848), C32e(0x6be4b8b8), - C32e(0x255d9f9f), C32e(0x616ebdbd), C32e(0x86ef4343), C32e(0x93a6c4c4), - C32e(0x72a83939), C32e(0x62a43131), C32e(0xbd37d3d3), C32e(0xff8bf2f2), - C32e(0xb132d5d5), C32e(0x0d438b8b), C32e(0xdc596e6e), C32e(0xafb7dada), - C32e(0x028c0101), C32e(0x7964b1b1), C32e(0x23d29c9c), C32e(0x92e04949), - C32e(0xabb4d8d8), C32e(0x43faacac), C32e(0xfd07f3f3), C32e(0x8525cfcf), - C32e(0x8fafcaca), C32e(0xf38ef4f4), C32e(0x8ee94747), C32e(0x20181010), - C32e(0xded56f6f), C32e(0xfb88f0f0), C32e(0x946f4a4a), C32e(0xb8725c5c), - C32e(0x70243838), C32e(0xaef15757), C32e(0xe6c77373), C32e(0x35519797), - C32e(0x8d23cbcb), C32e(0x597ca1a1), C32e(0xcb9ce8e8), C32e(0x7c213e3e), - C32e(0x37dd9696), C32e(0xc2dc6161), C32e(0x1a860d0d), C32e(0x1e850f0f), - C32e(0xdb90e0e0), C32e(0xf8427c7c), C32e(0xe2c47171), C32e(0x83aacccc), - C32e(0x3bd89090), C32e(0x0c050606), C32e(0xf501f7f7), C32e(0x38121c1c), - C32e(0x9fa3c2c2), C32e(0xd45f6a6a), C32e(0x47f9aeae), C32e(0xd2d06969), - C32e(0x2e911717), C32e(0x29589999), C32e(0x74273a3a), C32e(0x4eb92727), - C32e(0xa938d9d9), C32e(0xcd13ebeb), C32e(0x56b32b2b), C32e(0x44332222), - C32e(0xbfbbd2d2), C32e(0x4970a9a9), C32e(0x0e890707), C32e(0x66a73333), - C32e(0x5ab62d2d), C32e(0x78223c3c), C32e(0x2a921515), C32e(0x8920c9c9), - C32e(0x15498787), C32e(0x4fffaaaa), C32e(0xa0785050), C32e(0x517aa5a5), - C32e(0x068f0303), C32e(0xb2f85959), C32e(0x12800909), C32e(0x34171a1a), - C32e(0xcada6565), C32e(0xb531d7d7), C32e(0x13c68484), C32e(0xbbb8d0d0), - C32e(0x1fc38282), C32e(0x52b02929), C32e(0xb4775a5a), C32e(0x3c111e1e), - C32e(0xf6cb7b7b), C32e(0x4bfca8a8), C32e(0xdad66d6d), C32e(0x583a2c2c) -}; - -static const sph_u32 T3dn[] = { - C32e(0x32f4a5f4), C32e(0x6f978497), C32e(0x5eb099b0), C32e(0x7a8c8d8c), - C32e(0xe8170d17), C32e(0x0adcbddc), C32e(0x16c8b1c8), C32e(0x6dfc54fc), - C32e(0x90f050f0), C32e(0x07050305), C32e(0x2ee0a9e0), C32e(0xd1877d87), - C32e(0xcc2b192b), C32e(0x13a662a6), C32e(0x7c31e631), C32e(0x59b59ab5), - C32e(0x40cf45cf), C32e(0xa3bc9dbc), C32e(0x49c040c0), C32e(0x68928792), - C32e(0xd03f153f), C32e(0x9426eb26), C32e(0xce40c940), C32e(0xe61d0b1d), - C32e(0x6e2fec2f), C32e(0x1aa967a9), C32e(0x431cfd1c), C32e(0x6025ea25), - C32e(0xf9dabfda), C32e(0x5102f702), C32e(0x45a196a1), C32e(0x76ed5bed), - C32e(0x285dc25d), C32e(0xc5241c24), C32e(0xd4e9aee9), C32e(0xf2be6abe), - C32e(0x82ee5aee), C32e(0xbdc341c3), C32e(0xf3060206), C32e(0x52d14fd1), - C32e(0x8ce45ce4), C32e(0x5607f407), C32e(0x8d5c345c), C32e(0xe1180818), - C32e(0x4cae93ae), C32e(0x3e957395), C32e(0x97f553f5), C32e(0x6b413f41), - C32e(0x1c140c14), C32e(0x63f652f6), C32e(0xe9af65af), C32e(0x7fe25ee2), - C32e(0x48782878), C32e(0xcff8a1f8), C32e(0x1b110f11), C32e(0xebc4b5c4), - C32e(0x151b091b), C32e(0x7e5a365a), C32e(0xadb69bb6), C32e(0x98473d47), - C32e(0xa76a266a), C32e(0xf5bb69bb), C32e(0x334ccd4c), C32e(0x50ba9fba), - C32e(0x3f2d1b2d), C32e(0xa4b99eb9), C32e(0xc49c749c), C32e(0x46722e72), - C32e(0x41772d77), C32e(0x11cdb2cd), C32e(0x9d29ee29), C32e(0x4d16fb16), - C32e(0xa501f601), C32e(0xa1d74dd7), C32e(0x14a361a3), C32e(0x3449ce49), - C32e(0xdf8d7b8d), C32e(0x9f423e42), C32e(0xcd937193), C32e(0xb1a297a2), - C32e(0xa204f504), C32e(0x01b868b8), C32e(0x00000000), C32e(0xb5742c74), - C32e(0xe0a060a0), C32e(0xc2211f21), C32e(0x3a43c843), C32e(0x9a2ced2c), - C32e(0x0dd9bed9), C32e(0x47ca46ca), C32e(0x1770d970), C32e(0xafdd4bdd), - C32e(0xed79de79), C32e(0xff67d467), C32e(0x9323e823), C32e(0x5bde4ade), - C32e(0x06bd6bbd), C32e(0xbb7e2a7e), C32e(0x7b34e534), C32e(0xd73a163a), - C32e(0xd254c554), C32e(0xf862d762), C32e(0x99ff55ff), C32e(0xb6a794a7), - C32e(0xc04acf4a), C32e(0xd9301030), C32e(0x0e0a060a), C32e(0x66988198), - C32e(0xab0bf00b), C32e(0xb4cc44cc), C32e(0xf0d5bad5), C32e(0x753ee33e), - C32e(0xac0ef30e), C32e(0x4419fe19), C32e(0xdb5bc05b), C32e(0x80858a85), - C32e(0xd3ecadec), C32e(0xfedfbcdf), C32e(0xa8d848d8), C32e(0xfd0c040c), - C32e(0x197adf7a), C32e(0x2f58c158), C32e(0x309f759f), C32e(0xe7a563a5), - C32e(0x70503050), C32e(0xcb2e1a2e), C32e(0xef120e12), C32e(0x08b76db7), - C32e(0x55d44cd4), C32e(0x243c143c), C32e(0x795f355f), C32e(0xb2712f71), - C32e(0x8638e138), C32e(0xc8fda2fd), C32e(0xc74fcc4f), C32e(0x654b394b), - C32e(0x6af957f9), C32e(0x580df20d), C32e(0x619d829d), C32e(0xb3c947c9), - C32e(0x27efacef), C32e(0x8832e732), C32e(0x4f7d2b7d), C32e(0x42a495a4), - C32e(0x3bfba0fb), C32e(0xaab398b3), C32e(0xf668d168), C32e(0x22817f81), - C32e(0xeeaa66aa), C32e(0xd6827e82), C32e(0xdde6abe6), C32e(0x959e839e), - C32e(0xc945ca45), C32e(0xbc7b297b), C32e(0x056ed36e), C32e(0x6c443c44), - C32e(0x2c8b798b), C32e(0x813de23d), C32e(0x31271d27), C32e(0x379a769a), - C32e(0x964d3b4d), C32e(0x9efa56fa), C32e(0xa6d24ed2), C32e(0x36221e22), - C32e(0xe476db76), C32e(0x121e0a1e), C32e(0xfcb46cb4), C32e(0x8f37e437), - C32e(0x78e75de7), C32e(0x0fb26eb2), C32e(0x692aef2a), C32e(0x35f1a6f1), - C32e(0xdae3a8e3), C32e(0xc6f7a4f7), C32e(0x8a593759), C32e(0x74868b86), - C32e(0x83563256), C32e(0x4ec543c5), C32e(0x85eb59eb), C32e(0x18c2b7c2), - C32e(0x8e8f8c8f), C32e(0x1dac64ac), C32e(0xf16dd26d), C32e(0x723be03b), - C32e(0x1fc7b4c7), C32e(0xb915fa15), C32e(0xfa090709), C32e(0xa06f256f), - C32e(0x20eaafea), C32e(0x7d898e89), C32e(0x6720e920), C32e(0x38281828), - C32e(0x0b64d564), C32e(0x73838883), C32e(0xfbb16fb1), C32e(0xca967296), - C32e(0x546c246c), C32e(0x5f08f108), C32e(0x2152c752), C32e(0x64f351f3), - C32e(0xae652365), C32e(0x25847c84), C32e(0x57bf9cbf), C32e(0x5d632163), - C32e(0xea7cdd7c), C32e(0x1e7fdc7f), C32e(0x9c918691), C32e(0x9b948594), - C32e(0x4bab90ab), C32e(0xbac642c6), C32e(0x2657c457), C32e(0x29e5aae5), - C32e(0xe373d873), C32e(0x090f050f), C32e(0xf4030103), C32e(0x2a361236), - C32e(0x3cfea3fe), C32e(0x8be15fe1), C32e(0xbe10f910), C32e(0x026bd06b), - C32e(0xbfa891a8), C32e(0x71e858e8), C32e(0x53692769), C32e(0xf7d0b9d0), - C32e(0x91483848), C32e(0xde351335), C32e(0xe5ceb3ce), C32e(0x77553355), - C32e(0x04d6bbd6), C32e(0x39907090), C32e(0x87808980), C32e(0xc1f2a7f2), - C32e(0xecc1b6c1), C32e(0x5a662266), C32e(0xb8ad92ad), C32e(0xa9602060), - C32e(0x5cdb49db), C32e(0xb01aff1a), C32e(0xd8887888), C32e(0x2b8e7a8e), - C32e(0x898a8f8a), C32e(0x4a13f813), C32e(0x929b809b), C32e(0x23391739), - C32e(0x1075da75), C32e(0x84533153), C32e(0xd551c651), C32e(0x03d3b8d3), - C32e(0xdc5ec35e), C32e(0xe2cbb0cb), C32e(0xc3997799), C32e(0x2d331133), - C32e(0x3d46cb46), C32e(0xb71ffc1f), C32e(0x0c61d661), C32e(0x624e3a4e) -}; - -#define DECL_STATE_SMALL \ - sph_u32 H[16]; - -#define READ_STATE_SMALL(sc) do { \ - memcpy(H, (sc)->state.narrow, sizeof H); \ - } while (0) - -#define WRITE_STATE_SMALL(sc) do { \ - memcpy((sc)->state.narrow, H, sizeof H); \ - } while (0) - -#define XCAT(x, y) XCAT_(x, y) -#define XCAT_(x, y) x ## y - -#define RSTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ - t[d0] = T0up[B32_0(a[b0])] \ - ^ T1up[B32_1(a[b1])] \ - ^ T2up[B32_2(a[b2])] \ - ^ T3up[B32_3(a[b3])] \ - ^ T0dn[B32_0(a[b4])] \ - ^ T1dn[B32_1(a[b5])] \ - ^ T2dn[B32_2(a[b6])] \ - ^ T3dn[B32_3(a[b7])]; \ - t[d1] = T0dn[B32_0(a[b0])] \ - ^ T1dn[B32_1(a[b1])] \ - ^ T2dn[B32_2(a[b2])] \ - ^ T3dn[B32_3(a[b3])] \ - ^ T0up[B32_0(a[b4])] \ - ^ T1up[B32_1(a[b5])] \ - ^ T2up[B32_2(a[b6])] \ - ^ T3up[B32_3(a[b7])]; \ - } while (0) - -#define ROUND_SMALL_P(a, r) do { \ - sph_u32 t[16]; \ - a[0x0] ^= PC32up(0x00, r); \ - a[0x1] ^= PC32dn(0x00, r); \ - a[0x2] ^= PC32up(0x10, r); \ - a[0x3] ^= PC32dn(0x10, r); \ - a[0x4] ^= PC32up(0x20, r); \ - a[0x5] ^= PC32dn(0x20, r); \ - a[0x6] ^= PC32up(0x30, r); \ - a[0x7] ^= PC32dn(0x30, r); \ - a[0x8] ^= PC32up(0x40, r); \ - a[0x9] ^= PC32dn(0x40, r); \ - a[0xA] ^= PC32up(0x50, r); \ - a[0xB] ^= PC32dn(0x50, r); \ - a[0xC] ^= PC32up(0x60, r); \ - a[0xD] ^= PC32dn(0x60, r); \ - a[0xE] ^= PC32up(0x70, r); \ - a[0xF] ^= PC32dn(0x70, r); \ - RSTT(0x0, 0x1, a, 0x0, 0x2, 0x4, 0x6, 0x9, 0xB, 0xD, 0xF); \ - RSTT(0x2, 0x3, a, 0x2, 0x4, 0x6, 0x8, 0xB, 0xD, 0xF, 0x1); \ - RSTT(0x4, 0x5, a, 0x4, 0x6, 0x8, 0xA, 0xD, 0xF, 0x1, 0x3); \ - RSTT(0x6, 0x7, a, 0x6, 0x8, 0xA, 0xC, 0xF, 0x1, 0x3, 0x5); \ - RSTT(0x8, 0x9, a, 0x8, 0xA, 0xC, 0xE, 0x1, 0x3, 0x5, 0x7); \ - RSTT(0xA, 0xB, a, 0xA, 0xC, 0xE, 0x0, 0x3, 0x5, 0x7, 0x9); \ - RSTT(0xC, 0xD, a, 0xC, 0xE, 0x0, 0x2, 0x5, 0x7, 0x9, 0xB); \ - RSTT(0xE, 0xF, a, 0xE, 0x0, 0x2, 0x4, 0x7, 0x9, 0xB, 0xD); \ - memcpy(a, t, sizeof t); \ - } while (0) - -#define ROUND_SMALL_Q(a, r) do { \ - sph_u32 t[16]; \ - a[0x0] ^= QC32up(0x00, r); \ - a[0x1] ^= QC32dn(0x00, r); \ - a[0x2] ^= QC32up(0x10, r); \ - a[0x3] ^= QC32dn(0x10, r); \ - a[0x4] ^= QC32up(0x20, r); \ - a[0x5] ^= QC32dn(0x20, r); \ - a[0x6] ^= QC32up(0x30, r); \ - a[0x7] ^= QC32dn(0x30, r); \ - a[0x8] ^= QC32up(0x40, r); \ - a[0x9] ^= QC32dn(0x40, r); \ - a[0xA] ^= QC32up(0x50, r); \ - a[0xB] ^= QC32dn(0x50, r); \ - a[0xC] ^= QC32up(0x60, r); \ - a[0xD] ^= QC32dn(0x60, r); \ - a[0xE] ^= QC32up(0x70, r); \ - a[0xF] ^= QC32dn(0x70, r); \ - RSTT(0x0, 0x1, a, 0x2, 0x6, 0xA, 0xE, 0x1, 0x5, 0x9, 0xD); \ - RSTT(0x2, 0x3, a, 0x4, 0x8, 0xC, 0x0, 0x3, 0x7, 0xB, 0xF); \ - RSTT(0x4, 0x5, a, 0x6, 0xA, 0xE, 0x2, 0x5, 0x9, 0xD, 0x1); \ - RSTT(0x6, 0x7, a, 0x8, 0xC, 0x0, 0x4, 0x7, 0xB, 0xF, 0x3); \ - RSTT(0x8, 0x9, a, 0xA, 0xE, 0x2, 0x6, 0x9, 0xD, 0x1, 0x5); \ - RSTT(0xA, 0xB, a, 0xC, 0x0, 0x4, 0x8, 0xB, 0xF, 0x3, 0x7); \ - RSTT(0xC, 0xD, a, 0xE, 0x2, 0x6, 0xA, 0xD, 0x1, 0x5, 0x9); \ - RSTT(0xE, 0xF, a, 0x0, 0x4, 0x8, 0xC, 0xF, 0x3, 0x7, 0xB); \ - memcpy(a, t, sizeof t); \ - } while (0) - -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define PERM_SMALL_P(a) do { \ - int r; \ - for (r = 0; r < 10; r ++) \ - ROUND_SMALL_P(a, r); \ - } while (0) - -#define PERM_SMALL_Q(a) do { \ - int r; \ - for (r = 0; r < 10; r ++) \ - ROUND_SMALL_Q(a, r); \ - } while (0) - -#else - -#define PERM_SMALL_P(a) do { \ - int r; \ - for (r = 0; r < 10; r += 2) { \ - ROUND_SMALL_P(a, r + 0); \ - ROUND_SMALL_P(a, r + 1); \ - } \ - } while (0) - -#define PERM_SMALL_Q(a) do { \ - int r; \ - for (r = 0; r < 10; r += 2) { \ - ROUND_SMALL_Q(a, r + 0); \ - ROUND_SMALL_Q(a, r + 1); \ - } \ - } while (0) - -#endif - -#define COMPRESS_SMALL do { \ - sph_u32 g[16], m[16]; \ - size_t u; \ - for (u = 0; u < 16; u ++) { \ - m[u] = dec32e_aligned(buf + (u << 2)); \ - g[u] = m[u] ^ H[u]; \ - } \ - PERM_SMALL_P(g); \ - PERM_SMALL_Q(m); \ - for (u = 0; u < 16; u ++) \ - H[u] ^= g[u] ^ m[u]; \ - } while (0) - -#define FINAL_SMALL do { \ - sph_u32 x[16]; \ - size_t u; \ - memcpy(x, H, sizeof x); \ - PERM_SMALL_P(x); \ - for (u = 0; u < 16; u ++) \ - H[u] ^= x[u]; \ - } while (0) - -#define DECL_STATE_BIG \ - sph_u32 H[32]; - -#define READ_STATE_BIG(sc) do { \ - memcpy(H, (sc)->state.narrow, sizeof H); \ - } while (0) - -#define WRITE_STATE_BIG(sc) do { \ - memcpy((sc)->state.narrow, H, sizeof H); \ - } while (0) - -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define RBTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ - sph_u32 fu2 = T0up[B32_2(a[b2])]; \ - sph_u32 fd2 = T0dn[B32_2(a[b2])]; \ - sph_u32 fu3 = T1up[B32_3(a[b3])]; \ - sph_u32 fd3 = T1dn[B32_3(a[b3])]; \ - sph_u32 fu6 = T0up[B32_2(a[b6])]; \ - sph_u32 fd6 = T0dn[B32_2(a[b6])]; \ - sph_u32 fu7 = T1up[B32_3(a[b7])]; \ - sph_u32 fd7 = T1dn[B32_3(a[b7])]; \ - t[d0] = T0up[B32_0(a[b0])] \ - ^ T1up[B32_1(a[b1])] \ - ^ R32u(fu2, fd2) \ - ^ R32u(fu3, fd3) \ - ^ T0dn[B32_0(a[b4])] \ - ^ T1dn[B32_1(a[b5])] \ - ^ R32d(fu6, fd6) \ - ^ R32d(fu7, fd7); \ - t[d1] = T0dn[B32_0(a[b0])] \ - ^ T1dn[B32_1(a[b1])] \ - ^ R32d(fu2, fd2) \ - ^ R32d(fu3, fd3) \ - ^ T0up[B32_0(a[b4])] \ - ^ T1up[B32_1(a[b5])] \ - ^ R32u(fu6, fd6) \ - ^ R32u(fu7, fd7); \ - } while (0) - -#else - -#define RBTT(d0, d1, a, b0, b1, b2, b3, b4, b5, b6, b7) do { \ - t[d0] = T0up[B32_0(a[b0])] \ - ^ T1up[B32_1(a[b1])] \ - ^ T2up[B32_2(a[b2])] \ - ^ T3up[B32_3(a[b3])] \ - ^ T0dn[B32_0(a[b4])] \ - ^ T1dn[B32_1(a[b5])] \ - ^ T2dn[B32_2(a[b6])] \ - ^ T3dn[B32_3(a[b7])]; \ - t[d1] = T0dn[B32_0(a[b0])] \ - ^ T1dn[B32_1(a[b1])] \ - ^ T2dn[B32_2(a[b2])] \ - ^ T3dn[B32_3(a[b3])] \ - ^ T0up[B32_0(a[b4])] \ - ^ T1up[B32_1(a[b5])] \ - ^ T2up[B32_2(a[b6])] \ - ^ T3up[B32_3(a[b7])]; \ - } while (0) - -#endif - -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define ROUND_BIG_P(a, r) do { \ - sph_u32 t[32]; \ - size_t u; \ - a[0x00] ^= PC32up(0x00, r); \ - a[0x01] ^= PC32dn(0x00, r); \ - a[0x02] ^= PC32up(0x10, r); \ - a[0x03] ^= PC32dn(0x10, r); \ - a[0x04] ^= PC32up(0x20, r); \ - a[0x05] ^= PC32dn(0x20, r); \ - a[0x06] ^= PC32up(0x30, r); \ - a[0x07] ^= PC32dn(0x30, r); \ - a[0x08] ^= PC32up(0x40, r); \ - a[0x09] ^= PC32dn(0x40, r); \ - a[0x0A] ^= PC32up(0x50, r); \ - a[0x0B] ^= PC32dn(0x50, r); \ - a[0x0C] ^= PC32up(0x60, r); \ - a[0x0D] ^= PC32dn(0x60, r); \ - a[0x0E] ^= PC32up(0x70, r); \ - a[0x0F] ^= PC32dn(0x70, r); \ - a[0x10] ^= PC32up(0x80, r); \ - a[0x11] ^= PC32dn(0x80, r); \ - a[0x12] ^= PC32up(0x90, r); \ - a[0x13] ^= PC32dn(0x90, r); \ - a[0x14] ^= PC32up(0xA0, r); \ - a[0x15] ^= PC32dn(0xA0, r); \ - a[0x16] ^= PC32up(0xB0, r); \ - a[0x17] ^= PC32dn(0xB0, r); \ - a[0x18] ^= PC32up(0xC0, r); \ - a[0x19] ^= PC32dn(0xC0, r); \ - a[0x1A] ^= PC32up(0xD0, r); \ - a[0x1B] ^= PC32dn(0xD0, r); \ - a[0x1C] ^= PC32up(0xE0, r); \ - a[0x1D] ^= PC32dn(0xE0, r); \ - a[0x1E] ^= PC32up(0xF0, r); \ - a[0x1F] ^= PC32dn(0xF0, r); \ - for (u = 0; u < 32; u += 8) { \ - RBTT(u + 0x00, (u + 0x01) & 0x1F, a, \ - u + 0x00, (u + 0x02) & 0x1F, \ - (u + 0x04) & 0x1F, (u + 0x06) & 0x1F, \ - (u + 0x09) & 0x1F, (u + 0x0B) & 0x1F, \ - (u + 0x0D) & 0x1F, (u + 0x17) & 0x1F); \ - RBTT(u + 0x02, (u + 0x03) & 0x1F, a, \ - u + 0x02, (u + 0x04) & 0x1F, \ - (u + 0x06) & 0x1F, (u + 0x08) & 0x1F, \ - (u + 0x0B) & 0x1F, (u + 0x0D) & 0x1F, \ - (u + 0x0F) & 0x1F, (u + 0x19) & 0x1F); \ - RBTT(u + 0x04, (u + 0x05) & 0x1F, a, \ - u + 0x04, (u + 0x06) & 0x1F, \ - (u + 0x08) & 0x1F, (u + 0x0A) & 0x1F, \ - (u + 0x0D) & 0x1F, (u + 0x0F) & 0x1F, \ - (u + 0x11) & 0x1F, (u + 0x1B) & 0x1F); \ - RBTT(u + 0x06, (u + 0x07) & 0x1F, a, \ - u + 0x06, (u + 0x08) & 0x1F, \ - (u + 0x0A) & 0x1F, (u + 0x0C) & 0x1F, \ - (u + 0x0F) & 0x1F, (u + 0x11) & 0x1F, \ - (u + 0x13) & 0x1F, (u + 0x1D) & 0x1F); \ - } \ - memcpy(a, t, sizeof t); \ - } while (0) - -#define ROUND_BIG_Q(a, r) do { \ - sph_u32 t[32]; \ - size_t u; \ - a[0x00] ^= QC32up(0x00, r); \ - a[0x01] ^= QC32dn(0x00, r); \ - a[0x02] ^= QC32up(0x10, r); \ - a[0x03] ^= QC32dn(0x10, r); \ - a[0x04] ^= QC32up(0x20, r); \ - a[0x05] ^= QC32dn(0x20, r); \ - a[0x06] ^= QC32up(0x30, r); \ - a[0x07] ^= QC32dn(0x30, r); \ - a[0x08] ^= QC32up(0x40, r); \ - a[0x09] ^= QC32dn(0x40, r); \ - a[0x0A] ^= QC32up(0x50, r); \ - a[0x0B] ^= QC32dn(0x50, r); \ - a[0x0C] ^= QC32up(0x60, r); \ - a[0x0D] ^= QC32dn(0x60, r); \ - a[0x0E] ^= QC32up(0x70, r); \ - a[0x0F] ^= QC32dn(0x70, r); \ - a[0x10] ^= QC32up(0x80, r); \ - a[0x11] ^= QC32dn(0x80, r); \ - a[0x12] ^= QC32up(0x90, r); \ - a[0x13] ^= QC32dn(0x90, r); \ - a[0x14] ^= QC32up(0xA0, r); \ - a[0x15] ^= QC32dn(0xA0, r); \ - a[0x16] ^= QC32up(0xB0, r); \ - a[0x17] ^= QC32dn(0xB0, r); \ - a[0x18] ^= QC32up(0xC0, r); \ - a[0x19] ^= QC32dn(0xC0, r); \ - a[0x1A] ^= QC32up(0xD0, r); \ - a[0x1B] ^= QC32dn(0xD0, r); \ - a[0x1C] ^= QC32up(0xE0, r); \ - a[0x1D] ^= QC32dn(0xE0, r); \ - a[0x1E] ^= QC32up(0xF0, r); \ - a[0x1F] ^= QC32dn(0xF0, r); \ - for (u = 0; u < 32; u += 8) { \ - RBTT(u + 0x00, (u + 0x01) & 0x1F, a, \ - (u + 0x02) & 0x1F, (u + 0x06) & 0x1F, \ - (u + 0x0A) & 0x1F, (u + 0x16) & 0x1F, \ - (u + 0x01) & 0x1F, (u + 0x05) & 0x1F, \ - (u + 0x09) & 0x1F, (u + 0x0D) & 0x1F); \ - RBTT(u + 0x02, (u + 0x03) & 0x1F, a, \ - (u + 0x04) & 0x1F, (u + 0x08) & 0x1F, \ - (u + 0x0C) & 0x1F, (u + 0x18) & 0x1F, \ - (u + 0x03) & 0x1F, (u + 0x07) & 0x1F, \ - (u + 0x0B) & 0x1F, (u + 0x0F) & 0x1F); \ - RBTT(u + 0x04, (u + 0x05) & 0x1F, a, \ - (u + 0x06) & 0x1F, (u + 0x0A) & 0x1F, \ - (u + 0x0E) & 0x1F, (u + 0x1A) & 0x1F, \ - (u + 0x05) & 0x1F, (u + 0x09) & 0x1F, \ - (u + 0x0D) & 0x1F, (u + 0x11) & 0x1F); \ - RBTT(u + 0x06, (u + 0x07) & 0x1F, a, \ - (u + 0x08) & 0x1F, (u + 0x0C) & 0x1F, \ - (u + 0x10) & 0x1F, (u + 0x1C) & 0x1F, \ - (u + 0x07) & 0x1F, (u + 0x0B) & 0x1F, \ - (u + 0x0F) & 0x1F, (u + 0x13) & 0x1F); \ - } \ - memcpy(a, t, sizeof t); \ - } while (0) - -#else - -#define ROUND_BIG_P(a, r) do { \ - sph_u32 t[32]; \ - a[0x00] ^= PC32up(0x00, r); \ - a[0x01] ^= PC32dn(0x00, r); \ - a[0x02] ^= PC32up(0x10, r); \ - a[0x03] ^= PC32dn(0x10, r); \ - a[0x04] ^= PC32up(0x20, r); \ - a[0x05] ^= PC32dn(0x20, r); \ - a[0x06] ^= PC32up(0x30, r); \ - a[0x07] ^= PC32dn(0x30, r); \ - a[0x08] ^= PC32up(0x40, r); \ - a[0x09] ^= PC32dn(0x40, r); \ - a[0x0A] ^= PC32up(0x50, r); \ - a[0x0B] ^= PC32dn(0x50, r); \ - a[0x0C] ^= PC32up(0x60, r); \ - a[0x0D] ^= PC32dn(0x60, r); \ - a[0x0E] ^= PC32up(0x70, r); \ - a[0x0F] ^= PC32dn(0x70, r); \ - a[0x10] ^= PC32up(0x80, r); \ - a[0x11] ^= PC32dn(0x80, r); \ - a[0x12] ^= PC32up(0x90, r); \ - a[0x13] ^= PC32dn(0x90, r); \ - a[0x14] ^= PC32up(0xA0, r); \ - a[0x15] ^= PC32dn(0xA0, r); \ - a[0x16] ^= PC32up(0xB0, r); \ - a[0x17] ^= PC32dn(0xB0, r); \ - a[0x18] ^= PC32up(0xC0, r); \ - a[0x19] ^= PC32dn(0xC0, r); \ - a[0x1A] ^= PC32up(0xD0, r); \ - a[0x1B] ^= PC32dn(0xD0, r); \ - a[0x1C] ^= PC32up(0xE0, r); \ - a[0x1D] ^= PC32dn(0xE0, r); \ - a[0x1E] ^= PC32up(0xF0, r); \ - a[0x1F] ^= PC32dn(0xF0, r); \ - RBTT(0x00, 0x01, a, \ - 0x00, 0x02, 0x04, 0x06, 0x09, 0x0B, 0x0D, 0x17); \ - RBTT(0x02, 0x03, a, \ - 0x02, 0x04, 0x06, 0x08, 0x0B, 0x0D, 0x0F, 0x19); \ - RBTT(0x04, 0x05, a, \ - 0x04, 0x06, 0x08, 0x0A, 0x0D, 0x0F, 0x11, 0x1B); \ - RBTT(0x06, 0x07, a, \ - 0x06, 0x08, 0x0A, 0x0C, 0x0F, 0x11, 0x13, 0x1D); \ - RBTT(0x08, 0x09, a, \ - 0x08, 0x0A, 0x0C, 0x0E, 0x11, 0x13, 0x15, 0x1F); \ - RBTT(0x0A, 0x0B, a, \ - 0x0A, 0x0C, 0x0E, 0x10, 0x13, 0x15, 0x17, 0x01); \ - RBTT(0x0C, 0x0D, a, \ - 0x0C, 0x0E, 0x10, 0x12, 0x15, 0x17, 0x19, 0x03); \ - RBTT(0x0E, 0x0F, a, \ - 0x0E, 0x10, 0x12, 0x14, 0x17, 0x19, 0x1B, 0x05); \ - RBTT(0x10, 0x11, a, \ - 0x10, 0x12, 0x14, 0x16, 0x19, 0x1B, 0x1D, 0x07); \ - RBTT(0x12, 0x13, a, \ - 0x12, 0x14, 0x16, 0x18, 0x1B, 0x1D, 0x1F, 0x09); \ - RBTT(0x14, 0x15, a, \ - 0x14, 0x16, 0x18, 0x1A, 0x1D, 0x1F, 0x01, 0x0B); \ - RBTT(0x16, 0x17, a, \ - 0x16, 0x18, 0x1A, 0x1C, 0x1F, 0x01, 0x03, 0x0D); \ - RBTT(0x18, 0x19, a, \ - 0x18, 0x1A, 0x1C, 0x1E, 0x01, 0x03, 0x05, 0x0F); \ - RBTT(0x1A, 0x1B, a, \ - 0x1A, 0x1C, 0x1E, 0x00, 0x03, 0x05, 0x07, 0x11); \ - RBTT(0x1C, 0x1D, a, \ - 0x1C, 0x1E, 0x00, 0x02, 0x05, 0x07, 0x09, 0x13); \ - RBTT(0x1E, 0x1F, a, \ - 0x1E, 0x00, 0x02, 0x04, 0x07, 0x09, 0x0B, 0x15); \ - memcpy(a, t, sizeof t); \ - } while (0) - -#define ROUND_BIG_Q(a, r) do { \ - sph_u32 t[32]; \ - a[0x00] ^= QC32up(0x00, r); \ - a[0x01] ^= QC32dn(0x00, r); \ - a[0x02] ^= QC32up(0x10, r); \ - a[0x03] ^= QC32dn(0x10, r); \ - a[0x04] ^= QC32up(0x20, r); \ - a[0x05] ^= QC32dn(0x20, r); \ - a[0x06] ^= QC32up(0x30, r); \ - a[0x07] ^= QC32dn(0x30, r); \ - a[0x08] ^= QC32up(0x40, r); \ - a[0x09] ^= QC32dn(0x40, r); \ - a[0x0A] ^= QC32up(0x50, r); \ - a[0x0B] ^= QC32dn(0x50, r); \ - a[0x0C] ^= QC32up(0x60, r); \ - a[0x0D] ^= QC32dn(0x60, r); \ - a[0x0E] ^= QC32up(0x70, r); \ - a[0x0F] ^= QC32dn(0x70, r); \ - a[0x10] ^= QC32up(0x80, r); \ - a[0x11] ^= QC32dn(0x80, r); \ - a[0x12] ^= QC32up(0x90, r); \ - a[0x13] ^= QC32dn(0x90, r); \ - a[0x14] ^= QC32up(0xA0, r); \ - a[0x15] ^= QC32dn(0xA0, r); \ - a[0x16] ^= QC32up(0xB0, r); \ - a[0x17] ^= QC32dn(0xB0, r); \ - a[0x18] ^= QC32up(0xC0, r); \ - a[0x19] ^= QC32dn(0xC0, r); \ - a[0x1A] ^= QC32up(0xD0, r); \ - a[0x1B] ^= QC32dn(0xD0, r); \ - a[0x1C] ^= QC32up(0xE0, r); \ - a[0x1D] ^= QC32dn(0xE0, r); \ - a[0x1E] ^= QC32up(0xF0, r); \ - a[0x1F] ^= QC32dn(0xF0, r); \ - RBTT(0x00, 0x01, a, \ - 0x02, 0x06, 0x0A, 0x16, 0x01, 0x05, 0x09, 0x0D); \ - RBTT(0x02, 0x03, a, \ - 0x04, 0x08, 0x0C, 0x18, 0x03, 0x07, 0x0B, 0x0F); \ - RBTT(0x04, 0x05, a, \ - 0x06, 0x0A, 0x0E, 0x1A, 0x05, 0x09, 0x0D, 0x11); \ - RBTT(0x06, 0x07, a, \ - 0x08, 0x0C, 0x10, 0x1C, 0x07, 0x0B, 0x0F, 0x13); \ - RBTT(0x08, 0x09, a, \ - 0x0A, 0x0E, 0x12, 0x1E, 0x09, 0x0D, 0x11, 0x15); \ - RBTT(0x0A, 0x0B, a, \ - 0x0C, 0x10, 0x14, 0x00, 0x0B, 0x0F, 0x13, 0x17); \ - RBTT(0x0C, 0x0D, a, \ - 0x0E, 0x12, 0x16, 0x02, 0x0D, 0x11, 0x15, 0x19); \ - RBTT(0x0E, 0x0F, a, \ - 0x10, 0x14, 0x18, 0x04, 0x0F, 0x13, 0x17, 0x1B); \ - RBTT(0x10, 0x11, a, \ - 0x12, 0x16, 0x1A, 0x06, 0x11, 0x15, 0x19, 0x1D); \ - RBTT(0x12, 0x13, a, \ - 0x14, 0x18, 0x1C, 0x08, 0x13, 0x17, 0x1B, 0x1F); \ - RBTT(0x14, 0x15, a, \ - 0x16, 0x1A, 0x1E, 0x0A, 0x15, 0x19, 0x1D, 0x01); \ - RBTT(0x16, 0x17, a, \ - 0x18, 0x1C, 0x00, 0x0C, 0x17, 0x1B, 0x1F, 0x03); \ - RBTT(0x18, 0x19, a, \ - 0x1A, 0x1E, 0x02, 0x0E, 0x19, 0x1D, 0x01, 0x05); \ - RBTT(0x1A, 0x1B, a, \ - 0x1C, 0x00, 0x04, 0x10, 0x1B, 0x1F, 0x03, 0x07); \ - RBTT(0x1C, 0x1D, a, \ - 0x1E, 0x02, 0x06, 0x12, 0x1D, 0x01, 0x05, 0x09); \ - RBTT(0x1E, 0x1F, a, \ - 0x00, 0x04, 0x08, 0x14, 0x1F, 0x03, 0x07, 0x0B); \ - memcpy(a, t, sizeof t); \ - } while (0) - -#endif - -#if SPH_SMALL_FOOTPRINT_GROESTL - -#define PERM_BIG_P(a) do { \ - int r; \ - for (r = 0; r < 14; r ++) \ - ROUND_BIG_P(a, r); \ - } while (0) - -#define PERM_BIG_Q(a) do { \ - int r; \ - for (r = 0; r < 14; r ++) \ - ROUND_BIG_Q(a, r); \ - } while (0) - -#else - -#define PERM_BIG_P(a) do { \ - int r; \ - for (r = 0; r < 14; r += 2) { \ - ROUND_BIG_P(a, r + 0); \ - ROUND_BIG_P(a, r + 1); \ - } \ - } while (0) - -#define PERM_BIG_Q(a) do { \ - int r; \ - for (r = 0; r < 14; r += 2) { \ - ROUND_BIG_Q(a, r + 0); \ - ROUND_BIG_Q(a, r + 1); \ - } \ - } while (0) - -#endif - -#define COMPRESS_BIG do { \ - sph_u32 g[32], m[32]; \ - size_t u; \ - for (u = 0; u < 32; u ++) { \ - m[u] = dec32e_aligned(buf + (u << 2)); \ - g[u] = m[u] ^ H[u]; \ - } \ - PERM_BIG_P(g); \ - PERM_BIG_Q(m); \ - for (u = 0; u < 32; u ++) \ - H[u] ^= g[u] ^ m[u]; \ - } while (0) - -#define FINAL_BIG do { \ - sph_u32 x[32]; \ - size_t u; \ - memcpy(x, H, sizeof x); \ - PERM_BIG_P(x); \ - for (u = 0; u < 32; u ++) \ - H[u] ^= x[u]; \ - } while (0) - -#endif - -static void -groestl_small_init(sph_groestl_small_context *sc, unsigned out_size) -{ - size_t u; - - sc->ptr = 0; -#if SPH_GROESTL_64 - for (u = 0; u < 7; u ++) - sc->state.wide[u] = 0; -#if USE_LE - sc->state.wide[7] = ((sph_u64)(out_size & 0xFF) << 56) - | ((sph_u64)(out_size & 0xFF00) << 40); -#else - sc->state.wide[7] = (sph_u64)out_size; -#endif -#else - for (u = 0; u < 15; u ++) - sc->state.narrow[u] = 0; -#if USE_LE - sc->state.narrow[15] = ((sph_u32)(out_size & 0xFF) << 24) - | ((sph_u32)(out_size & 0xFF00) << 8); -#else - sc->state.narrow[15] = (sph_u32)out_size; -#endif -#endif -#if SPH_64 - sc->count = 0; -#else - sc->count_high = 0; - sc->count_low = 0; -#endif -} - -static void -groestl_small_core(sph_groestl_small_context *sc, const void *data, size_t len) -{ - unsigned char *buf; - size_t ptr; - DECL_STATE_SMALL - - buf = sc->buf; - ptr = sc->ptr; - if (len < (sizeof sc->buf) - ptr) { - memcpy(buf + ptr, data, len); - ptr += len; - sc->ptr = ptr; - return; - } - - READ_STATE_SMALL(sc); - while (len > 0) { - size_t clen; - - clen = (sizeof sc->buf) - ptr; - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - ptr += clen; - data = (const unsigned char *)data + clen; - len -= clen; - if (ptr == sizeof sc->buf) { - COMPRESS_SMALL; -#if SPH_64 - sc->count ++; -#else - if ((sc->count_low = SPH_T32(sc->count_low + 1)) == 0) - sc->count_high = SPH_T32(sc->count_high + 1); -#endif - ptr = 0; - } - } - WRITE_STATE_SMALL(sc); - sc->ptr = ptr; -} - -static void -groestl_small_close(sph_groestl_small_context *sc, - unsigned ub, unsigned n, void *dst, size_t out_len) -{ - unsigned char *buf; - unsigned char pad[72]; - size_t u, ptr, pad_len; -#if SPH_64 - sph_u64 count; -#else - sph_u32 count_high, count_low; -#endif - unsigned z; - DECL_STATE_SMALL - - buf = sc->buf; - ptr = sc->ptr; - z = 0x80 >> n; - pad[0] = ((ub & -z) | z) & 0xFF; - if (ptr < 56) { - pad_len = 64 - ptr; -#if SPH_64 - count = SPH_T64(sc->count + 1); -#else - count_low = SPH_T32(sc->count_low + 1); - count_high = SPH_T32(sc->count_high); - if (count_low == 0) - count_high = SPH_T32(count_high + 1); -#endif - } else { - pad_len = 128 - ptr; -#if SPH_64 - count = SPH_T64(sc->count + 2); -#else - count_low = SPH_T32(sc->count_low + 2); - count_high = SPH_T32(sc->count_high); - if (count_low <= 1) - count_high = SPH_T32(count_high + 1); -#endif - } - memset(pad + 1, 0, pad_len - 9); -#if SPH_64 - sph_enc64be(pad + pad_len - 8, count); -#else - sph_enc64be(pad + pad_len - 8, count_high); - sph_enc64be(pad + pad_len - 4, count_low); -#endif - groestl_small_core(sc, pad, pad_len); - READ_STATE_SMALL(sc); - FINAL_SMALL; -#if SPH_GROESTL_64 - for (u = 0; u < 4; u ++) - enc64e(pad + (u << 3), H[u + 4]); -#else - for (u = 0; u < 8; u ++) - enc32e(pad + (u << 2), H[u + 8]); -#endif - memcpy(dst, pad + 32 - out_len, out_len); - groestl_small_init(sc, (unsigned)out_len << 3); -} - -static void -groestl_big_init(sph_groestl_big_context *sc, unsigned out_size) -{ - size_t u; - - sc->ptr = 0; -#if SPH_GROESTL_64 - for (u = 0; u < 15; u ++) - sc->state.wide[u] = 0; -#if USE_LE - sc->state.wide[15] = ((sph_u64)(out_size & 0xFF) << 56) - | ((sph_u64)(out_size & 0xFF00) << 40); -#else - sc->state.wide[15] = (sph_u64)out_size; -#endif -#else - for (u = 0; u < 31; u ++) - sc->state.narrow[u] = 0; -#if USE_LE - sc->state.narrow[31] = ((sph_u32)(out_size & 0xFF) << 24) - | ((sph_u32)(out_size & 0xFF00) << 8); -#else - sc->state.narrow[31] = (sph_u32)out_size; -#endif -#endif -#if SPH_64 - sc->count = 0; -#else - sc->count_high = 0; - sc->count_low = 0; -#endif -} - -static void -groestl_big_core(sph_groestl_big_context *sc, const void *data, size_t len) -{ - unsigned char *buf; - size_t ptr; - DECL_STATE_BIG - - buf = sc->buf; - ptr = sc->ptr; - if (len < (sizeof sc->buf) - ptr) { - memcpy(buf + ptr, data, len); - ptr += len; - sc->ptr = ptr; - return; - } - - READ_STATE_BIG(sc); - while (len > 0) { - size_t clen; - - clen = (sizeof sc->buf) - ptr; - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - ptr += clen; - data = (const unsigned char *)data + clen; - len -= clen; - if (ptr == sizeof sc->buf) { - COMPRESS_BIG; -#if SPH_64 - sc->count ++; -#else - if ((sc->count_low = SPH_T32(sc->count_low + 1)) == 0) - sc->count_high = SPH_T32(sc->count_high + 1); -#endif - ptr = 0; - } - } - WRITE_STATE_BIG(sc); - sc->ptr = ptr; -} - -static void -groestl_big_close(sph_groestl_big_context *sc, - unsigned ub, unsigned n, void *dst, size_t out_len) -{ - unsigned char *buf; - unsigned char pad[136]; - size_t ptr, pad_len, u; -#if SPH_64 - sph_u64 count; -#else - sph_u32 count_high, count_low; -#endif - unsigned z; - DECL_STATE_BIG - - buf = sc->buf; - ptr = sc->ptr; - z = 0x80 >> n; - pad[0] = ((ub & -z) | z) & 0xFF; - if (ptr < 120) { - pad_len = 128 - ptr; -#if SPH_64 - count = SPH_T64(sc->count + 1); -#else - count_low = SPH_T32(sc->count_low + 1); - count_high = SPH_T32(sc->count_high); - if (count_low == 0) - count_high = SPH_T32(count_high + 1); -#endif - } else { - pad_len = 256 - ptr; -#if SPH_64 - count = SPH_T64(sc->count + 2); -#else - count_low = SPH_T32(sc->count_low + 2); - count_high = SPH_T32(sc->count_high); - if (count_low <= 1) - count_high = SPH_T32(count_high + 1); -#endif - } - memset(pad + 1, 0, pad_len - 9); -#if SPH_64 - sph_enc64be(pad + pad_len - 8, count); -#else - sph_enc64be(pad + pad_len - 8, count_high); - sph_enc64be(pad + pad_len - 4, count_low); -#endif - groestl_big_core(sc, pad, pad_len); - READ_STATE_BIG(sc); - FINAL_BIG; -#if SPH_GROESTL_64 - for (u = 0; u < 8; u ++) - enc64e(pad + (u << 3), H[u + 8]); -#else - for (u = 0; u < 16; u ++) - enc32e(pad + (u << 2), H[u + 16]); -#endif - memcpy(dst, pad + 64 - out_len, out_len); - groestl_big_init(sc, (unsigned)out_len << 3); -} - -/* see sph_groestl.h */ -void -sph_groestl224_init(void *cc) -{ - groestl_small_init(cc, 224); -} - -/* see sph_groestl.h */ -void -sph_groestl224(void *cc, const void *data, size_t len) -{ - groestl_small_core(cc, data, len); -} - -/* see sph_groestl.h */ -void -sph_groestl224_close(void *cc, void *dst) -{ - groestl_small_close(cc, 0, 0, dst, 28); -} - -/* see sph_groestl.h */ -void -sph_groestl224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - groestl_small_close(cc, ub, n, dst, 28); -} - -/* see sph_groestl.h */ -void -sph_groestl256_init(void *cc) -{ - groestl_small_init(cc, 256); -} - -/* see sph_groestl.h */ -void -sph_groestl256(void *cc, const void *data, size_t len) -{ - groestl_small_core(cc, data, len); -} - -/* see sph_groestl.h */ -void -sph_groestl256_close(void *cc, void *dst) -{ - groestl_small_close(cc, 0, 0, dst, 32); -} - -/* see sph_groestl.h */ -void -sph_groestl256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - groestl_small_close(cc, ub, n, dst, 32); -} - -/* see sph_groestl.h */ -void -sph_groestl384_init(void *cc) -{ - groestl_big_init(cc, 384); -} - -/* see sph_groestl.h */ -void -sph_groestl384(void *cc, const void *data, size_t len) -{ - groestl_big_core(cc, data, len); -} - -/* see sph_groestl.h */ -void -sph_groestl384_close(void *cc, void *dst) -{ - groestl_big_close(cc, 0, 0, dst, 48); -} - -/* see sph_groestl.h */ -void -sph_groestl384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - groestl_big_close(cc, ub, n, dst, 48); -} - -/* see sph_groestl.h */ -void -sph_groestl512_init(void *cc) -{ - groestl_big_init(cc, 512); -} - -/* see sph_groestl.h */ -void -sph_groestl512(void *cc, const void *data, size_t len) -{ - groestl_big_core(cc, data, len); -} - -/* see sph_groestl.h */ -void -sph_groestl512_close(void *cc, void *dst) -{ - groestl_big_close(cc, 0, 0, dst, 64); -} - -/* see sph_groestl.h */ -void -sph_groestl512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - groestl_big_close(cc, ub, n, dst, 64); -} - -#ifdef __cplusplus -} -#endif diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c deleted file mode 100644 index cff9f87dae68..000000000000 --- a/src/crypto/keccak.c +++ /dev/null @@ -1,1824 +0,0 @@ -/* $Id: keccak.c 259 2011-07-19 22:11:27Z tp $ */ -/* - * Keccak implementation. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @author Thomas Pornin - */ - -#include -#include - -#include "sph_keccak.h" - -#ifdef __cplusplus -extern "C"{ -#endif - -/* - * Parameters: - * - * SPH_KECCAK_64 use a 64-bit type - * SPH_KECCAK_UNROLL number of loops to unroll (0/undef for full unroll) - * SPH_KECCAK_INTERLEAVE use bit-interleaving (32-bit type only) - * SPH_KECCAK_NOCOPY do not copy the state into local variables - * - * If there is no usable 64-bit type, the code automatically switches - * back to the 32-bit implementation. - * - * Some tests on an Intel Core2 Q6600 (both 64-bit and 32-bit, 32 kB L1 - * code cache), a PowerPC (G3, 32 kB L1 code cache), an ARM920T core - * (16 kB L1 code cache), and a small MIPS-compatible CPU (Broadcom BCM3302, - * 8 kB L1 code cache), seem to show that the following are optimal: - * - * -- x86, 64-bit: use the 64-bit implementation, unroll 8 rounds, - * do not copy the state; unrolling 2, 6 or all rounds also provides - * near-optimal performance. - * -- x86, 32-bit: use the 32-bit implementation, unroll 6 rounds, - * interleave, do not copy the state. Unrolling 1, 2, 4 or 8 rounds - * also provides near-optimal performance. - * -- PowerPC: use the 64-bit implementation, unroll 8 rounds, - * copy the state. Unrolling 4 or 6 rounds is near-optimal. - * -- ARM: use the 64-bit implementation, unroll 2 or 4 rounds, - * copy the state. - * -- MIPS: use the 64-bit implementation, unroll 2 rounds, copy - * the state. Unrolling only 1 round is also near-optimal. - * - * Also, interleaving does not always yield actual improvements when - * using a 32-bit implementation; in particular when the architecture - * does not offer a native rotation opcode (interleaving replaces one - * 64-bit rotation with two 32-bit rotations, which is a gain only if - * there is a native 32-bit rotation opcode and not a native 64-bit - * rotation opcode; also, interleaving implies a small overhead when - * processing input words). - * - * To sum up: - * -- when possible, use the 64-bit code - * -- exception: on 32-bit x86, use 32-bit code - * -- when using 32-bit code, use interleaving - * -- copy the state, except on x86 - * -- unroll 8 rounds on "big" machine, 2 rounds on "small" machines - */ - -#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_KECCAK -#define SPH_SMALL_FOOTPRINT_KECCAK 1 -#endif - -/* - * By default, we select the 64-bit implementation if a 64-bit type - * is available, unless a 32-bit x86 is detected. - */ -#if !defined SPH_KECCAK_64 && SPH_64 \ - && !(defined __i386__ || SPH_I386_GCC || SPH_I386_MSVC) -#define SPH_KECCAK_64 1 -#endif - -/* - * If using a 32-bit implementation, we prefer to interleave. - */ -#if !SPH_KECCAK_64 && !defined SPH_KECCAK_INTERLEAVE -#define SPH_KECCAK_INTERLEAVE 1 -#endif - -/* - * Unroll 8 rounds on big systems, 2 rounds on small systems. - */ -#ifndef SPH_KECCAK_UNROLL -#if SPH_SMALL_FOOTPRINT_KECCAK -#define SPH_KECCAK_UNROLL 2 -#else -#define SPH_KECCAK_UNROLL 8 -#endif -#endif - -/* - * We do not want to copy the state to local variables on x86 (32-bit - * and 64-bit alike). - */ -#ifndef SPH_KECCAK_NOCOPY -#if defined __i386__ || defined __x86_64 || SPH_I386_MSVC || SPH_I386_GCC -#define SPH_KECCAK_NOCOPY 1 -#else -#define SPH_KECCAK_NOCOPY 0 -#endif -#endif - -#ifdef _MSC_VER -#pragma warning (disable: 4146) -#endif - -#if SPH_KECCAK_64 - -static const sph_u64 RC[] = { - SPH_C64(0x0000000000000001), SPH_C64(0x0000000000008082), - SPH_C64(0x800000000000808A), SPH_C64(0x8000000080008000), - SPH_C64(0x000000000000808B), SPH_C64(0x0000000080000001), - SPH_C64(0x8000000080008081), SPH_C64(0x8000000000008009), - SPH_C64(0x000000000000008A), SPH_C64(0x0000000000000088), - SPH_C64(0x0000000080008009), SPH_C64(0x000000008000000A), - SPH_C64(0x000000008000808B), SPH_C64(0x800000000000008B), - SPH_C64(0x8000000000008089), SPH_C64(0x8000000000008003), - SPH_C64(0x8000000000008002), SPH_C64(0x8000000000000080), - SPH_C64(0x000000000000800A), SPH_C64(0x800000008000000A), - SPH_C64(0x8000000080008081), SPH_C64(0x8000000000008080), - SPH_C64(0x0000000080000001), SPH_C64(0x8000000080008008) -}; - -#if SPH_KECCAK_NOCOPY - -#define a00 (kc->u.wide[ 0]) -#define a10 (kc->u.wide[ 1]) -#define a20 (kc->u.wide[ 2]) -#define a30 (kc->u.wide[ 3]) -#define a40 (kc->u.wide[ 4]) -#define a01 (kc->u.wide[ 5]) -#define a11 (kc->u.wide[ 6]) -#define a21 (kc->u.wide[ 7]) -#define a31 (kc->u.wide[ 8]) -#define a41 (kc->u.wide[ 9]) -#define a02 (kc->u.wide[10]) -#define a12 (kc->u.wide[11]) -#define a22 (kc->u.wide[12]) -#define a32 (kc->u.wide[13]) -#define a42 (kc->u.wide[14]) -#define a03 (kc->u.wide[15]) -#define a13 (kc->u.wide[16]) -#define a23 (kc->u.wide[17]) -#define a33 (kc->u.wide[18]) -#define a43 (kc->u.wide[19]) -#define a04 (kc->u.wide[20]) -#define a14 (kc->u.wide[21]) -#define a24 (kc->u.wide[22]) -#define a34 (kc->u.wide[23]) -#define a44 (kc->u.wide[24]) - -#define DECL_STATE -#define READ_STATE(sc) -#define WRITE_STATE(sc) - -#define INPUT_BUF(size) do { \ - size_t j; \ - for (j = 0; j < (size); j += 8) { \ - kc->u.wide[j >> 3] ^= sph_dec64le_aligned(buf + j); \ - } \ - } while (0) - -#define INPUT_BUF144 INPUT_BUF(144) -#define INPUT_BUF136 INPUT_BUF(136) -#define INPUT_BUF104 INPUT_BUF(104) -#define INPUT_BUF72 INPUT_BUF(72) - -#else - -#define DECL_STATE \ - sph_u64 a00, a01, a02, a03, a04; \ - sph_u64 a10, a11, a12, a13, a14; \ - sph_u64 a20, a21, a22, a23, a24; \ - sph_u64 a30, a31, a32, a33, a34; \ - sph_u64 a40, a41, a42, a43, a44; - -#define READ_STATE(state) do { \ - a00 = (state)->u.wide[ 0]; \ - a10 = (state)->u.wide[ 1]; \ - a20 = (state)->u.wide[ 2]; \ - a30 = (state)->u.wide[ 3]; \ - a40 = (state)->u.wide[ 4]; \ - a01 = (state)->u.wide[ 5]; \ - a11 = (state)->u.wide[ 6]; \ - a21 = (state)->u.wide[ 7]; \ - a31 = (state)->u.wide[ 8]; \ - a41 = (state)->u.wide[ 9]; \ - a02 = (state)->u.wide[10]; \ - a12 = (state)->u.wide[11]; \ - a22 = (state)->u.wide[12]; \ - a32 = (state)->u.wide[13]; \ - a42 = (state)->u.wide[14]; \ - a03 = (state)->u.wide[15]; \ - a13 = (state)->u.wide[16]; \ - a23 = (state)->u.wide[17]; \ - a33 = (state)->u.wide[18]; \ - a43 = (state)->u.wide[19]; \ - a04 = (state)->u.wide[20]; \ - a14 = (state)->u.wide[21]; \ - a24 = (state)->u.wide[22]; \ - a34 = (state)->u.wide[23]; \ - a44 = (state)->u.wide[24]; \ - } while (0) - -#define WRITE_STATE(state) do { \ - (state)->u.wide[ 0] = a00; \ - (state)->u.wide[ 1] = a10; \ - (state)->u.wide[ 2] = a20; \ - (state)->u.wide[ 3] = a30; \ - (state)->u.wide[ 4] = a40; \ - (state)->u.wide[ 5] = a01; \ - (state)->u.wide[ 6] = a11; \ - (state)->u.wide[ 7] = a21; \ - (state)->u.wide[ 8] = a31; \ - (state)->u.wide[ 9] = a41; \ - (state)->u.wide[10] = a02; \ - (state)->u.wide[11] = a12; \ - (state)->u.wide[12] = a22; \ - (state)->u.wide[13] = a32; \ - (state)->u.wide[14] = a42; \ - (state)->u.wide[15] = a03; \ - (state)->u.wide[16] = a13; \ - (state)->u.wide[17] = a23; \ - (state)->u.wide[18] = a33; \ - (state)->u.wide[19] = a43; \ - (state)->u.wide[20] = a04; \ - (state)->u.wide[21] = a14; \ - (state)->u.wide[22] = a24; \ - (state)->u.wide[23] = a34; \ - (state)->u.wide[24] = a44; \ - } while (0) - -#define INPUT_BUF144 do { \ - a00 ^= sph_dec64le_aligned(buf + 0); \ - a10 ^= sph_dec64le_aligned(buf + 8); \ - a20 ^= sph_dec64le_aligned(buf + 16); \ - a30 ^= sph_dec64le_aligned(buf + 24); \ - a40 ^= sph_dec64le_aligned(buf + 32); \ - a01 ^= sph_dec64le_aligned(buf + 40); \ - a11 ^= sph_dec64le_aligned(buf + 48); \ - a21 ^= sph_dec64le_aligned(buf + 56); \ - a31 ^= sph_dec64le_aligned(buf + 64); \ - a41 ^= sph_dec64le_aligned(buf + 72); \ - a02 ^= sph_dec64le_aligned(buf + 80); \ - a12 ^= sph_dec64le_aligned(buf + 88); \ - a22 ^= sph_dec64le_aligned(buf + 96); \ - a32 ^= sph_dec64le_aligned(buf + 104); \ - a42 ^= sph_dec64le_aligned(buf + 112); \ - a03 ^= sph_dec64le_aligned(buf + 120); \ - a13 ^= sph_dec64le_aligned(buf + 128); \ - a23 ^= sph_dec64le_aligned(buf + 136); \ - } while (0) - -#define INPUT_BUF136 do { \ - a00 ^= sph_dec64le_aligned(buf + 0); \ - a10 ^= sph_dec64le_aligned(buf + 8); \ - a20 ^= sph_dec64le_aligned(buf + 16); \ - a30 ^= sph_dec64le_aligned(buf + 24); \ - a40 ^= sph_dec64le_aligned(buf + 32); \ - a01 ^= sph_dec64le_aligned(buf + 40); \ - a11 ^= sph_dec64le_aligned(buf + 48); \ - a21 ^= sph_dec64le_aligned(buf + 56); \ - a31 ^= sph_dec64le_aligned(buf + 64); \ - a41 ^= sph_dec64le_aligned(buf + 72); \ - a02 ^= sph_dec64le_aligned(buf + 80); \ - a12 ^= sph_dec64le_aligned(buf + 88); \ - a22 ^= sph_dec64le_aligned(buf + 96); \ - a32 ^= sph_dec64le_aligned(buf + 104); \ - a42 ^= sph_dec64le_aligned(buf + 112); \ - a03 ^= sph_dec64le_aligned(buf + 120); \ - a13 ^= sph_dec64le_aligned(buf + 128); \ - } while (0) - -#define INPUT_BUF104 do { \ - a00 ^= sph_dec64le_aligned(buf + 0); \ - a10 ^= sph_dec64le_aligned(buf + 8); \ - a20 ^= sph_dec64le_aligned(buf + 16); \ - a30 ^= sph_dec64le_aligned(buf + 24); \ - a40 ^= sph_dec64le_aligned(buf + 32); \ - a01 ^= sph_dec64le_aligned(buf + 40); \ - a11 ^= sph_dec64le_aligned(buf + 48); \ - a21 ^= sph_dec64le_aligned(buf + 56); \ - a31 ^= sph_dec64le_aligned(buf + 64); \ - a41 ^= sph_dec64le_aligned(buf + 72); \ - a02 ^= sph_dec64le_aligned(buf + 80); \ - a12 ^= sph_dec64le_aligned(buf + 88); \ - a22 ^= sph_dec64le_aligned(buf + 96); \ - } while (0) - -#define INPUT_BUF72 do { \ - a00 ^= sph_dec64le_aligned(buf + 0); \ - a10 ^= sph_dec64le_aligned(buf + 8); \ - a20 ^= sph_dec64le_aligned(buf + 16); \ - a30 ^= sph_dec64le_aligned(buf + 24); \ - a40 ^= sph_dec64le_aligned(buf + 32); \ - a01 ^= sph_dec64le_aligned(buf + 40); \ - a11 ^= sph_dec64le_aligned(buf + 48); \ - a21 ^= sph_dec64le_aligned(buf + 56); \ - a31 ^= sph_dec64le_aligned(buf + 64); \ - } while (0) - -#define INPUT_BUF(lim) do { \ - a00 ^= sph_dec64le_aligned(buf + 0); \ - a10 ^= sph_dec64le_aligned(buf + 8); \ - a20 ^= sph_dec64le_aligned(buf + 16); \ - a30 ^= sph_dec64le_aligned(buf + 24); \ - a40 ^= sph_dec64le_aligned(buf + 32); \ - a01 ^= sph_dec64le_aligned(buf + 40); \ - a11 ^= sph_dec64le_aligned(buf + 48); \ - a21 ^= sph_dec64le_aligned(buf + 56); \ - a31 ^= sph_dec64le_aligned(buf + 64); \ - if ((lim) == 72) \ - break; \ - a41 ^= sph_dec64le_aligned(buf + 72); \ - a02 ^= sph_dec64le_aligned(buf + 80); \ - a12 ^= sph_dec64le_aligned(buf + 88); \ - a22 ^= sph_dec64le_aligned(buf + 96); \ - if ((lim) == 104) \ - break; \ - a32 ^= sph_dec64le_aligned(buf + 104); \ - a42 ^= sph_dec64le_aligned(buf + 112); \ - a03 ^= sph_dec64le_aligned(buf + 120); \ - a13 ^= sph_dec64le_aligned(buf + 128); \ - if ((lim) == 136) \ - break; \ - a23 ^= sph_dec64le_aligned(buf + 136); \ - } while (0) - -#endif - -#define DECL64(x) sph_u64 x -#define MOV64(d, s) (d = s) -#define XOR64(d, a, b) (d = a ^ b) -#define AND64(d, a, b) (d = a & b) -#define OR64(d, a, b) (d = a | b) -#define NOT64(d, s) (d = SPH_T64(~s)) -#define ROL64(d, v, n) (d = SPH_ROTL64(v, n)) -#define XOR64_IOTA XOR64 - -#else - -static const struct { - sph_u32 high, low; -} RC[] = { -#if SPH_KECCAK_INTERLEAVE - { SPH_C32(0x00000000), SPH_C32(0x00000001) }, - { SPH_C32(0x00000089), SPH_C32(0x00000000) }, - { SPH_C32(0x8000008B), SPH_C32(0x00000000) }, - { SPH_C32(0x80008080), SPH_C32(0x00000000) }, - { SPH_C32(0x0000008B), SPH_C32(0x00000001) }, - { SPH_C32(0x00008000), SPH_C32(0x00000001) }, - { SPH_C32(0x80008088), SPH_C32(0x00000001) }, - { SPH_C32(0x80000082), SPH_C32(0x00000001) }, - { SPH_C32(0x0000000B), SPH_C32(0x00000000) }, - { SPH_C32(0x0000000A), SPH_C32(0x00000000) }, - { SPH_C32(0x00008082), SPH_C32(0x00000001) }, - { SPH_C32(0x00008003), SPH_C32(0x00000000) }, - { SPH_C32(0x0000808B), SPH_C32(0x00000001) }, - { SPH_C32(0x8000000B), SPH_C32(0x00000001) }, - { SPH_C32(0x8000008A), SPH_C32(0x00000001) }, - { SPH_C32(0x80000081), SPH_C32(0x00000001) }, - { SPH_C32(0x80000081), SPH_C32(0x00000000) }, - { SPH_C32(0x80000008), SPH_C32(0x00000000) }, - { SPH_C32(0x00000083), SPH_C32(0x00000000) }, - { SPH_C32(0x80008003), SPH_C32(0x00000000) }, - { SPH_C32(0x80008088), SPH_C32(0x00000001) }, - { SPH_C32(0x80000088), SPH_C32(0x00000000) }, - { SPH_C32(0x00008000), SPH_C32(0x00000001) }, - { SPH_C32(0x80008082), SPH_C32(0x00000000) } -#else - { SPH_C32(0x00000000), SPH_C32(0x00000001) }, - { SPH_C32(0x00000000), SPH_C32(0x00008082) }, - { SPH_C32(0x80000000), SPH_C32(0x0000808A) }, - { SPH_C32(0x80000000), SPH_C32(0x80008000) }, - { SPH_C32(0x00000000), SPH_C32(0x0000808B) }, - { SPH_C32(0x00000000), SPH_C32(0x80000001) }, - { SPH_C32(0x80000000), SPH_C32(0x80008081) }, - { SPH_C32(0x80000000), SPH_C32(0x00008009) }, - { SPH_C32(0x00000000), SPH_C32(0x0000008A) }, - { SPH_C32(0x00000000), SPH_C32(0x00000088) }, - { SPH_C32(0x00000000), SPH_C32(0x80008009) }, - { SPH_C32(0x00000000), SPH_C32(0x8000000A) }, - { SPH_C32(0x00000000), SPH_C32(0x8000808B) }, - { SPH_C32(0x80000000), SPH_C32(0x0000008B) }, - { SPH_C32(0x80000000), SPH_C32(0x00008089) }, - { SPH_C32(0x80000000), SPH_C32(0x00008003) }, - { SPH_C32(0x80000000), SPH_C32(0x00008002) }, - { SPH_C32(0x80000000), SPH_C32(0x00000080) }, - { SPH_C32(0x00000000), SPH_C32(0x0000800A) }, - { SPH_C32(0x80000000), SPH_C32(0x8000000A) }, - { SPH_C32(0x80000000), SPH_C32(0x80008081) }, - { SPH_C32(0x80000000), SPH_C32(0x00008080) }, - { SPH_C32(0x00000000), SPH_C32(0x80000001) }, - { SPH_C32(0x80000000), SPH_C32(0x80008008) } -#endif -}; - -#if SPH_KECCAK_INTERLEAVE - -#define INTERLEAVE(xl, xh) do { \ - sph_u32 l, h, t; \ - l = (xl); h = (xh); \ - t = (l ^ (l >> 1)) & SPH_C32(0x22222222); l ^= t ^ (t << 1); \ - t = (h ^ (h >> 1)) & SPH_C32(0x22222222); h ^= t ^ (t << 1); \ - t = (l ^ (l >> 2)) & SPH_C32(0x0C0C0C0C); l ^= t ^ (t << 2); \ - t = (h ^ (h >> 2)) & SPH_C32(0x0C0C0C0C); h ^= t ^ (t << 2); \ - t = (l ^ (l >> 4)) & SPH_C32(0x00F000F0); l ^= t ^ (t << 4); \ - t = (h ^ (h >> 4)) & SPH_C32(0x00F000F0); h ^= t ^ (t << 4); \ - t = (l ^ (l >> 8)) & SPH_C32(0x0000FF00); l ^= t ^ (t << 8); \ - t = (h ^ (h >> 8)) & SPH_C32(0x0000FF00); h ^= t ^ (t << 8); \ - t = (l ^ SPH_T32(h << 16)) & SPH_C32(0xFFFF0000); \ - l ^= t; h ^= t >> 16; \ - (xl) = l; (xh) = h; \ - } while (0) - -#define UNINTERLEAVE(xl, xh) do { \ - sph_u32 l, h, t; \ - l = (xl); h = (xh); \ - t = (l ^ SPH_T32(h << 16)) & SPH_C32(0xFFFF0000); \ - l ^= t; h ^= t >> 16; \ - t = (l ^ (l >> 8)) & SPH_C32(0x0000FF00); l ^= t ^ (t << 8); \ - t = (h ^ (h >> 8)) & SPH_C32(0x0000FF00); h ^= t ^ (t << 8); \ - t = (l ^ (l >> 4)) & SPH_C32(0x00F000F0); l ^= t ^ (t << 4); \ - t = (h ^ (h >> 4)) & SPH_C32(0x00F000F0); h ^= t ^ (t << 4); \ - t = (l ^ (l >> 2)) & SPH_C32(0x0C0C0C0C); l ^= t ^ (t << 2); \ - t = (h ^ (h >> 2)) & SPH_C32(0x0C0C0C0C); h ^= t ^ (t << 2); \ - t = (l ^ (l >> 1)) & SPH_C32(0x22222222); l ^= t ^ (t << 1); \ - t = (h ^ (h >> 1)) & SPH_C32(0x22222222); h ^= t ^ (t << 1); \ - (xl) = l; (xh) = h; \ - } while (0) - -#else - -#define INTERLEAVE(l, h) -#define UNINTERLEAVE(l, h) - -#endif - -#if SPH_KECCAK_NOCOPY - -#define a00l (kc->u.narrow[2 * 0 + 0]) -#define a00h (kc->u.narrow[2 * 0 + 1]) -#define a10l (kc->u.narrow[2 * 1 + 0]) -#define a10h (kc->u.narrow[2 * 1 + 1]) -#define a20l (kc->u.narrow[2 * 2 + 0]) -#define a20h (kc->u.narrow[2 * 2 + 1]) -#define a30l (kc->u.narrow[2 * 3 + 0]) -#define a30h (kc->u.narrow[2 * 3 + 1]) -#define a40l (kc->u.narrow[2 * 4 + 0]) -#define a40h (kc->u.narrow[2 * 4 + 1]) -#define a01l (kc->u.narrow[2 * 5 + 0]) -#define a01h (kc->u.narrow[2 * 5 + 1]) -#define a11l (kc->u.narrow[2 * 6 + 0]) -#define a11h (kc->u.narrow[2 * 6 + 1]) -#define a21l (kc->u.narrow[2 * 7 + 0]) -#define a21h (kc->u.narrow[2 * 7 + 1]) -#define a31l (kc->u.narrow[2 * 8 + 0]) -#define a31h (kc->u.narrow[2 * 8 + 1]) -#define a41l (kc->u.narrow[2 * 9 + 0]) -#define a41h (kc->u.narrow[2 * 9 + 1]) -#define a02l (kc->u.narrow[2 * 10 + 0]) -#define a02h (kc->u.narrow[2 * 10 + 1]) -#define a12l (kc->u.narrow[2 * 11 + 0]) -#define a12h (kc->u.narrow[2 * 11 + 1]) -#define a22l (kc->u.narrow[2 * 12 + 0]) -#define a22h (kc->u.narrow[2 * 12 + 1]) -#define a32l (kc->u.narrow[2 * 13 + 0]) -#define a32h (kc->u.narrow[2 * 13 + 1]) -#define a42l (kc->u.narrow[2 * 14 + 0]) -#define a42h (kc->u.narrow[2 * 14 + 1]) -#define a03l (kc->u.narrow[2 * 15 + 0]) -#define a03h (kc->u.narrow[2 * 15 + 1]) -#define a13l (kc->u.narrow[2 * 16 + 0]) -#define a13h (kc->u.narrow[2 * 16 + 1]) -#define a23l (kc->u.narrow[2 * 17 + 0]) -#define a23h (kc->u.narrow[2 * 17 + 1]) -#define a33l (kc->u.narrow[2 * 18 + 0]) -#define a33h (kc->u.narrow[2 * 18 + 1]) -#define a43l (kc->u.narrow[2 * 19 + 0]) -#define a43h (kc->u.narrow[2 * 19 + 1]) -#define a04l (kc->u.narrow[2 * 20 + 0]) -#define a04h (kc->u.narrow[2 * 20 + 1]) -#define a14l (kc->u.narrow[2 * 21 + 0]) -#define a14h (kc->u.narrow[2 * 21 + 1]) -#define a24l (kc->u.narrow[2 * 22 + 0]) -#define a24h (kc->u.narrow[2 * 22 + 1]) -#define a34l (kc->u.narrow[2 * 23 + 0]) -#define a34h (kc->u.narrow[2 * 23 + 1]) -#define a44l (kc->u.narrow[2 * 24 + 0]) -#define a44h (kc->u.narrow[2 * 24 + 1]) - -#define DECL_STATE -#define READ_STATE(state) -#define WRITE_STATE(state) - -#define INPUT_BUF(size) do { \ - size_t j; \ - for (j = 0; j < (size); j += 8) { \ - sph_u32 tl, th; \ - tl = sph_dec32le_aligned(buf + j + 0); \ - th = sph_dec32le_aligned(buf + j + 4); \ - INTERLEAVE(tl, th); \ - kc->u.narrow[(j >> 2) + 0] ^= tl; \ - kc->u.narrow[(j >> 2) + 1] ^= th; \ - } \ - } while (0) - -#define INPUT_BUF144 INPUT_BUF(144) -#define INPUT_BUF136 INPUT_BUF(136) -#define INPUT_BUF104 INPUT_BUF(104) -#define INPUT_BUF72 INPUT_BUF(72) - -#else - -#define DECL_STATE \ - sph_u32 a00l, a00h, a01l, a01h, a02l, a02h, a03l, a03h, a04l, a04h; \ - sph_u32 a10l, a10h, a11l, a11h, a12l, a12h, a13l, a13h, a14l, a14h; \ - sph_u32 a20l, a20h, a21l, a21h, a22l, a22h, a23l, a23h, a24l, a24h; \ - sph_u32 a30l, a30h, a31l, a31h, a32l, a32h, a33l, a33h, a34l, a34h; \ - sph_u32 a40l, a40h, a41l, a41h, a42l, a42h, a43l, a43h, a44l, a44h; - -#define READ_STATE(state) do { \ - a00l = (state)->u.narrow[2 * 0 + 0]; \ - a00h = (state)->u.narrow[2 * 0 + 1]; \ - a10l = (state)->u.narrow[2 * 1 + 0]; \ - a10h = (state)->u.narrow[2 * 1 + 1]; \ - a20l = (state)->u.narrow[2 * 2 + 0]; \ - a20h = (state)->u.narrow[2 * 2 + 1]; \ - a30l = (state)->u.narrow[2 * 3 + 0]; \ - a30h = (state)->u.narrow[2 * 3 + 1]; \ - a40l = (state)->u.narrow[2 * 4 + 0]; \ - a40h = (state)->u.narrow[2 * 4 + 1]; \ - a01l = (state)->u.narrow[2 * 5 + 0]; \ - a01h = (state)->u.narrow[2 * 5 + 1]; \ - a11l = (state)->u.narrow[2 * 6 + 0]; \ - a11h = (state)->u.narrow[2 * 6 + 1]; \ - a21l = (state)->u.narrow[2 * 7 + 0]; \ - a21h = (state)->u.narrow[2 * 7 + 1]; \ - a31l = (state)->u.narrow[2 * 8 + 0]; \ - a31h = (state)->u.narrow[2 * 8 + 1]; \ - a41l = (state)->u.narrow[2 * 9 + 0]; \ - a41h = (state)->u.narrow[2 * 9 + 1]; \ - a02l = (state)->u.narrow[2 * 10 + 0]; \ - a02h = (state)->u.narrow[2 * 10 + 1]; \ - a12l = (state)->u.narrow[2 * 11 + 0]; \ - a12h = (state)->u.narrow[2 * 11 + 1]; \ - a22l = (state)->u.narrow[2 * 12 + 0]; \ - a22h = (state)->u.narrow[2 * 12 + 1]; \ - a32l = (state)->u.narrow[2 * 13 + 0]; \ - a32h = (state)->u.narrow[2 * 13 + 1]; \ - a42l = (state)->u.narrow[2 * 14 + 0]; \ - a42h = (state)->u.narrow[2 * 14 + 1]; \ - a03l = (state)->u.narrow[2 * 15 + 0]; \ - a03h = (state)->u.narrow[2 * 15 + 1]; \ - a13l = (state)->u.narrow[2 * 16 + 0]; \ - a13h = (state)->u.narrow[2 * 16 + 1]; \ - a23l = (state)->u.narrow[2 * 17 + 0]; \ - a23h = (state)->u.narrow[2 * 17 + 1]; \ - a33l = (state)->u.narrow[2 * 18 + 0]; \ - a33h = (state)->u.narrow[2 * 18 + 1]; \ - a43l = (state)->u.narrow[2 * 19 + 0]; \ - a43h = (state)->u.narrow[2 * 19 + 1]; \ - a04l = (state)->u.narrow[2 * 20 + 0]; \ - a04h = (state)->u.narrow[2 * 20 + 1]; \ - a14l = (state)->u.narrow[2 * 21 + 0]; \ - a14h = (state)->u.narrow[2 * 21 + 1]; \ - a24l = (state)->u.narrow[2 * 22 + 0]; \ - a24h = (state)->u.narrow[2 * 22 + 1]; \ - a34l = (state)->u.narrow[2 * 23 + 0]; \ - a34h = (state)->u.narrow[2 * 23 + 1]; \ - a44l = (state)->u.narrow[2 * 24 + 0]; \ - a44h = (state)->u.narrow[2 * 24 + 1]; \ - } while (0) - -#define WRITE_STATE(state) do { \ - (state)->u.narrow[2 * 0 + 0] = a00l; \ - (state)->u.narrow[2 * 0 + 1] = a00h; \ - (state)->u.narrow[2 * 1 + 0] = a10l; \ - (state)->u.narrow[2 * 1 + 1] = a10h; \ - (state)->u.narrow[2 * 2 + 0] = a20l; \ - (state)->u.narrow[2 * 2 + 1] = a20h; \ - (state)->u.narrow[2 * 3 + 0] = a30l; \ - (state)->u.narrow[2 * 3 + 1] = a30h; \ - (state)->u.narrow[2 * 4 + 0] = a40l; \ - (state)->u.narrow[2 * 4 + 1] = a40h; \ - (state)->u.narrow[2 * 5 + 0] = a01l; \ - (state)->u.narrow[2 * 5 + 1] = a01h; \ - (state)->u.narrow[2 * 6 + 0] = a11l; \ - (state)->u.narrow[2 * 6 + 1] = a11h; \ - (state)->u.narrow[2 * 7 + 0] = a21l; \ - (state)->u.narrow[2 * 7 + 1] = a21h; \ - (state)->u.narrow[2 * 8 + 0] = a31l; \ - (state)->u.narrow[2 * 8 + 1] = a31h; \ - (state)->u.narrow[2 * 9 + 0] = a41l; \ - (state)->u.narrow[2 * 9 + 1] = a41h; \ - (state)->u.narrow[2 * 10 + 0] = a02l; \ - (state)->u.narrow[2 * 10 + 1] = a02h; \ - (state)->u.narrow[2 * 11 + 0] = a12l; \ - (state)->u.narrow[2 * 11 + 1] = a12h; \ - (state)->u.narrow[2 * 12 + 0] = a22l; \ - (state)->u.narrow[2 * 12 + 1] = a22h; \ - (state)->u.narrow[2 * 13 + 0] = a32l; \ - (state)->u.narrow[2 * 13 + 1] = a32h; \ - (state)->u.narrow[2 * 14 + 0] = a42l; \ - (state)->u.narrow[2 * 14 + 1] = a42h; \ - (state)->u.narrow[2 * 15 + 0] = a03l; \ - (state)->u.narrow[2 * 15 + 1] = a03h; \ - (state)->u.narrow[2 * 16 + 0] = a13l; \ - (state)->u.narrow[2 * 16 + 1] = a13h; \ - (state)->u.narrow[2 * 17 + 0] = a23l; \ - (state)->u.narrow[2 * 17 + 1] = a23h; \ - (state)->u.narrow[2 * 18 + 0] = a33l; \ - (state)->u.narrow[2 * 18 + 1] = a33h; \ - (state)->u.narrow[2 * 19 + 0] = a43l; \ - (state)->u.narrow[2 * 19 + 1] = a43h; \ - (state)->u.narrow[2 * 20 + 0] = a04l; \ - (state)->u.narrow[2 * 20 + 1] = a04h; \ - (state)->u.narrow[2 * 21 + 0] = a14l; \ - (state)->u.narrow[2 * 21 + 1] = a14h; \ - (state)->u.narrow[2 * 22 + 0] = a24l; \ - (state)->u.narrow[2 * 22 + 1] = a24h; \ - (state)->u.narrow[2 * 23 + 0] = a34l; \ - (state)->u.narrow[2 * 23 + 1] = a34h; \ - (state)->u.narrow[2 * 24 + 0] = a44l; \ - (state)->u.narrow[2 * 24 + 1] = a44h; \ - } while (0) - -#define READ64(d, off) do { \ - sph_u32 tl, th; \ - tl = sph_dec32le_aligned(buf + (off)); \ - th = sph_dec32le_aligned(buf + (off) + 4); \ - INTERLEAVE(tl, th); \ - d ## l ^= tl; \ - d ## h ^= th; \ - } while (0) - -#define INPUT_BUF144 do { \ - READ64(a00, 0); \ - READ64(a10, 8); \ - READ64(a20, 16); \ - READ64(a30, 24); \ - READ64(a40, 32); \ - READ64(a01, 40); \ - READ64(a11, 48); \ - READ64(a21, 56); \ - READ64(a31, 64); \ - READ64(a41, 72); \ - READ64(a02, 80); \ - READ64(a12, 88); \ - READ64(a22, 96); \ - READ64(a32, 104); \ - READ64(a42, 112); \ - READ64(a03, 120); \ - READ64(a13, 128); \ - READ64(a23, 136); \ - } while (0) - -#define INPUT_BUF136 do { \ - READ64(a00, 0); \ - READ64(a10, 8); \ - READ64(a20, 16); \ - READ64(a30, 24); \ - READ64(a40, 32); \ - READ64(a01, 40); \ - READ64(a11, 48); \ - READ64(a21, 56); \ - READ64(a31, 64); \ - READ64(a41, 72); \ - READ64(a02, 80); \ - READ64(a12, 88); \ - READ64(a22, 96); \ - READ64(a32, 104); \ - READ64(a42, 112); \ - READ64(a03, 120); \ - READ64(a13, 128); \ - } while (0) - -#define INPUT_BUF104 do { \ - READ64(a00, 0); \ - READ64(a10, 8); \ - READ64(a20, 16); \ - READ64(a30, 24); \ - READ64(a40, 32); \ - READ64(a01, 40); \ - READ64(a11, 48); \ - READ64(a21, 56); \ - READ64(a31, 64); \ - READ64(a41, 72); \ - READ64(a02, 80); \ - READ64(a12, 88); \ - READ64(a22, 96); \ - } while (0) - -#define INPUT_BUF72 do { \ - READ64(a00, 0); \ - READ64(a10, 8); \ - READ64(a20, 16); \ - READ64(a30, 24); \ - READ64(a40, 32); \ - READ64(a01, 40); \ - READ64(a11, 48); \ - READ64(a21, 56); \ - READ64(a31, 64); \ - } while (0) - -#define INPUT_BUF(lim) do { \ - READ64(a00, 0); \ - READ64(a10, 8); \ - READ64(a20, 16); \ - READ64(a30, 24); \ - READ64(a40, 32); \ - READ64(a01, 40); \ - READ64(a11, 48); \ - READ64(a21, 56); \ - READ64(a31, 64); \ - if ((lim) == 72) \ - break; \ - READ64(a41, 72); \ - READ64(a02, 80); \ - READ64(a12, 88); \ - READ64(a22, 96); \ - if ((lim) == 104) \ - break; \ - READ64(a32, 104); \ - READ64(a42, 112); \ - READ64(a03, 120); \ - READ64(a13, 128); \ - if ((lim) == 136) \ - break; \ - READ64(a23, 136); \ - } while (0) - -#endif - -#define DECL64(x) sph_u64 x ## l, x ## h -#define MOV64(d, s) (d ## l = s ## l, d ## h = s ## h) -#define XOR64(d, a, b) (d ## l = a ## l ^ b ## l, d ## h = a ## h ^ b ## h) -#define AND64(d, a, b) (d ## l = a ## l & b ## l, d ## h = a ## h & b ## h) -#define OR64(d, a, b) (d ## l = a ## l | b ## l, d ## h = a ## h | b ## h) -#define NOT64(d, s) (d ## l = SPH_T32(~s ## l), d ## h = SPH_T32(~s ## h)) -#define ROL64(d, v, n) ROL64_ ## n(d, v) - -#if SPH_KECCAK_INTERLEAVE - -#define ROL64_odd1(d, v) do { \ - sph_u32 tmp; \ - tmp = v ## l; \ - d ## l = SPH_T32(v ## h << 1) | (v ## h >> 31); \ - d ## h = tmp; \ - } while (0) - -#define ROL64_odd63(d, v) do { \ - sph_u32 tmp; \ - tmp = SPH_T32(v ## l << 31) | (v ## l >> 1); \ - d ## l = v ## h; \ - d ## h = tmp; \ - } while (0) - -#define ROL64_odd(d, v, n) do { \ - sph_u32 tmp; \ - tmp = SPH_T32(v ## l << (n - 1)) | (v ## l >> (33 - n)); \ - d ## l = SPH_T32(v ## h << n) | (v ## h >> (32 - n)); \ - d ## h = tmp; \ - } while (0) - -#define ROL64_even(d, v, n) do { \ - d ## l = SPH_T32(v ## l << n) | (v ## l >> (32 - n)); \ - d ## h = SPH_T32(v ## h << n) | (v ## h >> (32 - n)); \ - } while (0) - -#define ROL64_0(d, v) -#define ROL64_1(d, v) ROL64_odd1(d, v) -#define ROL64_2(d, v) ROL64_even(d, v, 1) -#define ROL64_3(d, v) ROL64_odd( d, v, 2) -#define ROL64_4(d, v) ROL64_even(d, v, 2) -#define ROL64_5(d, v) ROL64_odd( d, v, 3) -#define ROL64_6(d, v) ROL64_even(d, v, 3) -#define ROL64_7(d, v) ROL64_odd( d, v, 4) -#define ROL64_8(d, v) ROL64_even(d, v, 4) -#define ROL64_9(d, v) ROL64_odd( d, v, 5) -#define ROL64_10(d, v) ROL64_even(d, v, 5) -#define ROL64_11(d, v) ROL64_odd( d, v, 6) -#define ROL64_12(d, v) ROL64_even(d, v, 6) -#define ROL64_13(d, v) ROL64_odd( d, v, 7) -#define ROL64_14(d, v) ROL64_even(d, v, 7) -#define ROL64_15(d, v) ROL64_odd( d, v, 8) -#define ROL64_16(d, v) ROL64_even(d, v, 8) -#define ROL64_17(d, v) ROL64_odd( d, v, 9) -#define ROL64_18(d, v) ROL64_even(d, v, 9) -#define ROL64_19(d, v) ROL64_odd( d, v, 10) -#define ROL64_20(d, v) ROL64_even(d, v, 10) -#define ROL64_21(d, v) ROL64_odd( d, v, 11) -#define ROL64_22(d, v) ROL64_even(d, v, 11) -#define ROL64_23(d, v) ROL64_odd( d, v, 12) -#define ROL64_24(d, v) ROL64_even(d, v, 12) -#define ROL64_25(d, v) ROL64_odd( d, v, 13) -#define ROL64_26(d, v) ROL64_even(d, v, 13) -#define ROL64_27(d, v) ROL64_odd( d, v, 14) -#define ROL64_28(d, v) ROL64_even(d, v, 14) -#define ROL64_29(d, v) ROL64_odd( d, v, 15) -#define ROL64_30(d, v) ROL64_even(d, v, 15) -#define ROL64_31(d, v) ROL64_odd( d, v, 16) -#define ROL64_32(d, v) ROL64_even(d, v, 16) -#define ROL64_33(d, v) ROL64_odd( d, v, 17) -#define ROL64_34(d, v) ROL64_even(d, v, 17) -#define ROL64_35(d, v) ROL64_odd( d, v, 18) -#define ROL64_36(d, v) ROL64_even(d, v, 18) -#define ROL64_37(d, v) ROL64_odd( d, v, 19) -#define ROL64_38(d, v) ROL64_even(d, v, 19) -#define ROL64_39(d, v) ROL64_odd( d, v, 20) -#define ROL64_40(d, v) ROL64_even(d, v, 20) -#define ROL64_41(d, v) ROL64_odd( d, v, 21) -#define ROL64_42(d, v) ROL64_even(d, v, 21) -#define ROL64_43(d, v) ROL64_odd( d, v, 22) -#define ROL64_44(d, v) ROL64_even(d, v, 22) -#define ROL64_45(d, v) ROL64_odd( d, v, 23) -#define ROL64_46(d, v) ROL64_even(d, v, 23) -#define ROL64_47(d, v) ROL64_odd( d, v, 24) -#define ROL64_48(d, v) ROL64_even(d, v, 24) -#define ROL64_49(d, v) ROL64_odd( d, v, 25) -#define ROL64_50(d, v) ROL64_even(d, v, 25) -#define ROL64_51(d, v) ROL64_odd( d, v, 26) -#define ROL64_52(d, v) ROL64_even(d, v, 26) -#define ROL64_53(d, v) ROL64_odd( d, v, 27) -#define ROL64_54(d, v) ROL64_even(d, v, 27) -#define ROL64_55(d, v) ROL64_odd( d, v, 28) -#define ROL64_56(d, v) ROL64_even(d, v, 28) -#define ROL64_57(d, v) ROL64_odd( d, v, 29) -#define ROL64_58(d, v) ROL64_even(d, v, 29) -#define ROL64_59(d, v) ROL64_odd( d, v, 30) -#define ROL64_60(d, v) ROL64_even(d, v, 30) -#define ROL64_61(d, v) ROL64_odd( d, v, 31) -#define ROL64_62(d, v) ROL64_even(d, v, 31) -#define ROL64_63(d, v) ROL64_odd63(d, v) - -#else - -#define ROL64_small(d, v, n) do { \ - sph_u32 tmp; \ - tmp = SPH_T32(v ## l << n) | (v ## h >> (32 - n)); \ - d ## h = SPH_T32(v ## h << n) | (v ## l >> (32 - n)); \ - d ## l = tmp; \ - } while (0) - -#define ROL64_0(d, v) 0 -#define ROL64_1(d, v) ROL64_small(d, v, 1) -#define ROL64_2(d, v) ROL64_small(d, v, 2) -#define ROL64_3(d, v) ROL64_small(d, v, 3) -#define ROL64_4(d, v) ROL64_small(d, v, 4) -#define ROL64_5(d, v) ROL64_small(d, v, 5) -#define ROL64_6(d, v) ROL64_small(d, v, 6) -#define ROL64_7(d, v) ROL64_small(d, v, 7) -#define ROL64_8(d, v) ROL64_small(d, v, 8) -#define ROL64_9(d, v) ROL64_small(d, v, 9) -#define ROL64_10(d, v) ROL64_small(d, v, 10) -#define ROL64_11(d, v) ROL64_small(d, v, 11) -#define ROL64_12(d, v) ROL64_small(d, v, 12) -#define ROL64_13(d, v) ROL64_small(d, v, 13) -#define ROL64_14(d, v) ROL64_small(d, v, 14) -#define ROL64_15(d, v) ROL64_small(d, v, 15) -#define ROL64_16(d, v) ROL64_small(d, v, 16) -#define ROL64_17(d, v) ROL64_small(d, v, 17) -#define ROL64_18(d, v) ROL64_small(d, v, 18) -#define ROL64_19(d, v) ROL64_small(d, v, 19) -#define ROL64_20(d, v) ROL64_small(d, v, 20) -#define ROL64_21(d, v) ROL64_small(d, v, 21) -#define ROL64_22(d, v) ROL64_small(d, v, 22) -#define ROL64_23(d, v) ROL64_small(d, v, 23) -#define ROL64_24(d, v) ROL64_small(d, v, 24) -#define ROL64_25(d, v) ROL64_small(d, v, 25) -#define ROL64_26(d, v) ROL64_small(d, v, 26) -#define ROL64_27(d, v) ROL64_small(d, v, 27) -#define ROL64_28(d, v) ROL64_small(d, v, 28) -#define ROL64_29(d, v) ROL64_small(d, v, 29) -#define ROL64_30(d, v) ROL64_small(d, v, 30) -#define ROL64_31(d, v) ROL64_small(d, v, 31) - -#define ROL64_32(d, v) do { \ - sph_u32 tmp; \ - tmp = v ## l; \ - d ## l = v ## h; \ - d ## h = tmp; \ - } while (0) - -#define ROL64_big(d, v, n) do { \ - sph_u32 trl, trh; \ - ROL64_small(tr, v, n); \ - d ## h = trl; \ - d ## l = trh; \ - } while (0) - -#define ROL64_33(d, v) ROL64_big(d, v, 1) -#define ROL64_34(d, v) ROL64_big(d, v, 2) -#define ROL64_35(d, v) ROL64_big(d, v, 3) -#define ROL64_36(d, v) ROL64_big(d, v, 4) -#define ROL64_37(d, v) ROL64_big(d, v, 5) -#define ROL64_38(d, v) ROL64_big(d, v, 6) -#define ROL64_39(d, v) ROL64_big(d, v, 7) -#define ROL64_40(d, v) ROL64_big(d, v, 8) -#define ROL64_41(d, v) ROL64_big(d, v, 9) -#define ROL64_42(d, v) ROL64_big(d, v, 10) -#define ROL64_43(d, v) ROL64_big(d, v, 11) -#define ROL64_44(d, v) ROL64_big(d, v, 12) -#define ROL64_45(d, v) ROL64_big(d, v, 13) -#define ROL64_46(d, v) ROL64_big(d, v, 14) -#define ROL64_47(d, v) ROL64_big(d, v, 15) -#define ROL64_48(d, v) ROL64_big(d, v, 16) -#define ROL64_49(d, v) ROL64_big(d, v, 17) -#define ROL64_50(d, v) ROL64_big(d, v, 18) -#define ROL64_51(d, v) ROL64_big(d, v, 19) -#define ROL64_52(d, v) ROL64_big(d, v, 20) -#define ROL64_53(d, v) ROL64_big(d, v, 21) -#define ROL64_54(d, v) ROL64_big(d, v, 22) -#define ROL64_55(d, v) ROL64_big(d, v, 23) -#define ROL64_56(d, v) ROL64_big(d, v, 24) -#define ROL64_57(d, v) ROL64_big(d, v, 25) -#define ROL64_58(d, v) ROL64_big(d, v, 26) -#define ROL64_59(d, v) ROL64_big(d, v, 27) -#define ROL64_60(d, v) ROL64_big(d, v, 28) -#define ROL64_61(d, v) ROL64_big(d, v, 29) -#define ROL64_62(d, v) ROL64_big(d, v, 30) -#define ROL64_63(d, v) ROL64_big(d, v, 31) - -#endif - -#define XOR64_IOTA(d, s, k) \ - (d ## l = s ## l ^ k.low, d ## h = s ## h ^ k.high) - -#endif - -#define TH_ELT(t, c0, c1, c2, c3, c4, d0, d1, d2, d3, d4) do { \ - DECL64(tt0); \ - DECL64(tt1); \ - DECL64(tt2); \ - DECL64(tt3); \ - XOR64(tt0, d0, d1); \ - XOR64(tt1, d2, d3); \ - XOR64(tt0, tt0, d4); \ - XOR64(tt0, tt0, tt1); \ - ROL64(tt0, tt0, 1); \ - XOR64(tt2, c0, c1); \ - XOR64(tt3, c2, c3); \ - XOR64(tt0, tt0, c4); \ - XOR64(tt2, tt2, tt3); \ - XOR64(t, tt0, tt2); \ - } while (0) - -#define THETA(b00, b01, b02, b03, b04, b10, b11, b12, b13, b14, \ - b20, b21, b22, b23, b24, b30, b31, b32, b33, b34, \ - b40, b41, b42, b43, b44) \ - do { \ - DECL64(t0); \ - DECL64(t1); \ - DECL64(t2); \ - DECL64(t3); \ - DECL64(t4); \ - TH_ELT(t0, b40, b41, b42, b43, b44, b10, b11, b12, b13, b14); \ - TH_ELT(t1, b00, b01, b02, b03, b04, b20, b21, b22, b23, b24); \ - TH_ELT(t2, b10, b11, b12, b13, b14, b30, b31, b32, b33, b34); \ - TH_ELT(t3, b20, b21, b22, b23, b24, b40, b41, b42, b43, b44); \ - TH_ELT(t4, b30, b31, b32, b33, b34, b00, b01, b02, b03, b04); \ - XOR64(b00, b00, t0); \ - XOR64(b01, b01, t0); \ - XOR64(b02, b02, t0); \ - XOR64(b03, b03, t0); \ - XOR64(b04, b04, t0); \ - XOR64(b10, b10, t1); \ - XOR64(b11, b11, t1); \ - XOR64(b12, b12, t1); \ - XOR64(b13, b13, t1); \ - XOR64(b14, b14, t1); \ - XOR64(b20, b20, t2); \ - XOR64(b21, b21, t2); \ - XOR64(b22, b22, t2); \ - XOR64(b23, b23, t2); \ - XOR64(b24, b24, t2); \ - XOR64(b30, b30, t3); \ - XOR64(b31, b31, t3); \ - XOR64(b32, b32, t3); \ - XOR64(b33, b33, t3); \ - XOR64(b34, b34, t3); \ - XOR64(b40, b40, t4); \ - XOR64(b41, b41, t4); \ - XOR64(b42, b42, t4); \ - XOR64(b43, b43, t4); \ - XOR64(b44, b44, t4); \ - } while (0) - -#define RHO(b00, b01, b02, b03, b04, b10, b11, b12, b13, b14, \ - b20, b21, b22, b23, b24, b30, b31, b32, b33, b34, \ - b40, b41, b42, b43, b44) \ - do { \ - /* ROL64(b00, b00, 0); */ \ - ROL64(b01, b01, 36); \ - ROL64(b02, b02, 3); \ - ROL64(b03, b03, 41); \ - ROL64(b04, b04, 18); \ - ROL64(b10, b10, 1); \ - ROL64(b11, b11, 44); \ - ROL64(b12, b12, 10); \ - ROL64(b13, b13, 45); \ - ROL64(b14, b14, 2); \ - ROL64(b20, b20, 62); \ - ROL64(b21, b21, 6); \ - ROL64(b22, b22, 43); \ - ROL64(b23, b23, 15); \ - ROL64(b24, b24, 61); \ - ROL64(b30, b30, 28); \ - ROL64(b31, b31, 55); \ - ROL64(b32, b32, 25); \ - ROL64(b33, b33, 21); \ - ROL64(b34, b34, 56); \ - ROL64(b40, b40, 27); \ - ROL64(b41, b41, 20); \ - ROL64(b42, b42, 39); \ - ROL64(b43, b43, 8); \ - ROL64(b44, b44, 14); \ - } while (0) - -/* - * The KHI macro integrates the "lane complement" optimization. On input, - * some words are complemented: - * a00 a01 a02 a04 a13 a20 a21 a22 a30 a33 a34 a43 - * On output, the following words are complemented: - * a04 a10 a20 a22 a23 a31 - * - * The (implicit) permutation and the theta expansion will bring back - * the input mask for the next round. - */ - -#define KHI_XO(d, a, b, c) do { \ - DECL64(kt); \ - OR64(kt, b, c); \ - XOR64(d, a, kt); \ - } while (0) - -#define KHI_XA(d, a, b, c) do { \ - DECL64(kt); \ - AND64(kt, b, c); \ - XOR64(d, a, kt); \ - } while (0) - -#define KHI(b00, b01, b02, b03, b04, b10, b11, b12, b13, b14, \ - b20, b21, b22, b23, b24, b30, b31, b32, b33, b34, \ - b40, b41, b42, b43, b44) \ - do { \ - DECL64(c0); \ - DECL64(c1); \ - DECL64(c2); \ - DECL64(c3); \ - DECL64(c4); \ - DECL64(bnn); \ - NOT64(bnn, b20); \ - KHI_XO(c0, b00, b10, b20); \ - KHI_XO(c1, b10, bnn, b30); \ - KHI_XA(c2, b20, b30, b40); \ - KHI_XO(c3, b30, b40, b00); \ - KHI_XA(c4, b40, b00, b10); \ - MOV64(b00, c0); \ - MOV64(b10, c1); \ - MOV64(b20, c2); \ - MOV64(b30, c3); \ - MOV64(b40, c4); \ - NOT64(bnn, b41); \ - KHI_XO(c0, b01, b11, b21); \ - KHI_XA(c1, b11, b21, b31); \ - KHI_XO(c2, b21, b31, bnn); \ - KHI_XO(c3, b31, b41, b01); \ - KHI_XA(c4, b41, b01, b11); \ - MOV64(b01, c0); \ - MOV64(b11, c1); \ - MOV64(b21, c2); \ - MOV64(b31, c3); \ - MOV64(b41, c4); \ - NOT64(bnn, b32); \ - KHI_XO(c0, b02, b12, b22); \ - KHI_XA(c1, b12, b22, b32); \ - KHI_XA(c2, b22, bnn, b42); \ - KHI_XO(c3, bnn, b42, b02); \ - KHI_XA(c4, b42, b02, b12); \ - MOV64(b02, c0); \ - MOV64(b12, c1); \ - MOV64(b22, c2); \ - MOV64(b32, c3); \ - MOV64(b42, c4); \ - NOT64(bnn, b33); \ - KHI_XA(c0, b03, b13, b23); \ - KHI_XO(c1, b13, b23, b33); \ - KHI_XO(c2, b23, bnn, b43); \ - KHI_XA(c3, bnn, b43, b03); \ - KHI_XO(c4, b43, b03, b13); \ - MOV64(b03, c0); \ - MOV64(b13, c1); \ - MOV64(b23, c2); \ - MOV64(b33, c3); \ - MOV64(b43, c4); \ - NOT64(bnn, b14); \ - KHI_XA(c0, b04, bnn, b24); \ - KHI_XO(c1, bnn, b24, b34); \ - KHI_XA(c2, b24, b34, b44); \ - KHI_XO(c3, b34, b44, b04); \ - KHI_XA(c4, b44, b04, b14); \ - MOV64(b04, c0); \ - MOV64(b14, c1); \ - MOV64(b24, c2); \ - MOV64(b34, c3); \ - MOV64(b44, c4); \ - } while (0) - -#define IOTA(r) XOR64_IOTA(a00, a00, r) - -#define P0 a00, a01, a02, a03, a04, a10, a11, a12, a13, a14, a20, a21, \ - a22, a23, a24, a30, a31, a32, a33, a34, a40, a41, a42, a43, a44 -#define P1 a00, a30, a10, a40, a20, a11, a41, a21, a01, a31, a22, a02, \ - a32, a12, a42, a33, a13, a43, a23, a03, a44, a24, a04, a34, a14 -#define P2 a00, a33, a11, a44, a22, a41, a24, a02, a30, a13, a32, a10, \ - a43, a21, a04, a23, a01, a34, a12, a40, a14, a42, a20, a03, a31 -#define P3 a00, a23, a41, a14, a32, a24, a42, a10, a33, a01, a43, a11, \ - a34, a02, a20, a12, a30, a03, a21, a44, a31, a04, a22, a40, a13 -#define P4 a00, a12, a24, a31, a43, a42, a04, a11, a23, a30, a34, a41, \ - a03, a10, a22, a21, a33, a40, a02, a14, a13, a20, a32, a44, a01 -#define P5 a00, a21, a42, a13, a34, a04, a20, a41, a12, a33, a03, a24, \ - a40, a11, a32, a02, a23, a44, a10, a31, a01, a22, a43, a14, a30 -#define P6 a00, a02, a04, a01, a03, a20, a22, a24, a21, a23, a40, a42, \ - a44, a41, a43, a10, a12, a14, a11, a13, a30, a32, a34, a31, a33 -#define P7 a00, a10, a20, a30, a40, a22, a32, a42, a02, a12, a44, a04, \ - a14, a24, a34, a11, a21, a31, a41, a01, a33, a43, a03, a13, a23 -#define P8 a00, a11, a22, a33, a44, a32, a43, a04, a10, a21, a14, a20, \ - a31, a42, a03, a41, a02, a13, a24, a30, a23, a34, a40, a01, a12 -#define P9 a00, a41, a32, a23, a14, a43, a34, a20, a11, a02, a31, a22, \ - a13, a04, a40, a24, a10, a01, a42, a33, a12, a03, a44, a30, a21 -#define P10 a00, a24, a43, a12, a31, a34, a03, a22, a41, a10, a13, a32, \ - a01, a20, a44, a42, a11, a30, a04, a23, a21, a40, a14, a33, a02 -#define P11 a00, a42, a34, a21, a13, a03, a40, a32, a24, a11, a01, a43, \ - a30, a22, a14, a04, a41, a33, a20, a12, a02, a44, a31, a23, a10 -#define P12 a00, a04, a03, a02, a01, a40, a44, a43, a42, a41, a30, a34, \ - a33, a32, a31, a20, a24, a23, a22, a21, a10, a14, a13, a12, a11 -#define P13 a00, a20, a40, a10, a30, a44, a14, a34, a04, a24, a33, a03, \ - a23, a43, a13, a22, a42, a12, a32, a02, a11, a31, a01, a21, a41 -#define P14 a00, a22, a44, a11, a33, a14, a31, a03, a20, a42, a23, a40, \ - a12, a34, a01, a32, a04, a21, a43, a10, a41, a13, a30, a02, a24 -#define P15 a00, a32, a14, a41, a23, a31, a13, a40, a22, a04, a12, a44, \ - a21, a03, a30, a43, a20, a02, a34, a11, a24, a01, a33, a10, a42 -#define P16 a00, a43, a31, a24, a12, a13, a01, a44, a32, a20, a21, a14, \ - a02, a40, a33, a34, a22, a10, a03, a41, a42, a30, a23, a11, a04 -#define P17 a00, a34, a13, a42, a21, a01, a30, a14, a43, a22, a02, a31, \ - a10, a44, a23, a03, a32, a11, a40, a24, a04, a33, a12, a41, a20 -#define P18 a00, a03, a01, a04, a02, a30, a33, a31, a34, a32, a10, a13, \ - a11, a14, a12, a40, a43, a41, a44, a42, a20, a23, a21, a24, a22 -#define P19 a00, a40, a30, a20, a10, a33, a23, a13, a03, a43, a11, a01, \ - a41, a31, a21, a44, a34, a24, a14, a04, a22, a12, a02, a42, a32 -#define P20 a00, a44, a33, a22, a11, a23, a12, a01, a40, a34, a41, a30, \ - a24, a13, a02, a14, a03, a42, a31, a20, a32, a21, a10, a04, a43 -#define P21 a00, a14, a23, a32, a41, a12, a21, a30, a44, a03, a24, a33, \ - a42, a01, a10, a31, a40, a04, a13, a22, a43, a02, a11, a20, a34 -#define P22 a00, a31, a12, a43, a24, a21, a02, a33, a14, a40, a42, a23, \ - a04, a30, a11, a13, a44, a20, a01, a32, a34, a10, a41, a22, a03 -#define P23 a00, a13, a21, a34, a42, a02, a10, a23, a31, a44, a04, a12, \ - a20, a33, a41, a01, a14, a22, a30, a43, a03, a11, a24, a32, a40 - -#define P1_TO_P0 do { \ - DECL64(t); \ - MOV64(t, a01); \ - MOV64(a01, a30); \ - MOV64(a30, a33); \ - MOV64(a33, a23); \ - MOV64(a23, a12); \ - MOV64(a12, a21); \ - MOV64(a21, a02); \ - MOV64(a02, a10); \ - MOV64(a10, a11); \ - MOV64(a11, a41); \ - MOV64(a41, a24); \ - MOV64(a24, a42); \ - MOV64(a42, a04); \ - MOV64(a04, a20); \ - MOV64(a20, a22); \ - MOV64(a22, a32); \ - MOV64(a32, a43); \ - MOV64(a43, a34); \ - MOV64(a34, a03); \ - MOV64(a03, a40); \ - MOV64(a40, a44); \ - MOV64(a44, a14); \ - MOV64(a14, a31); \ - MOV64(a31, a13); \ - MOV64(a13, t); \ - } while (0) - -#define P2_TO_P0 do { \ - DECL64(t); \ - MOV64(t, a01); \ - MOV64(a01, a33); \ - MOV64(a33, a12); \ - MOV64(a12, a02); \ - MOV64(a02, a11); \ - MOV64(a11, a24); \ - MOV64(a24, a04); \ - MOV64(a04, a22); \ - MOV64(a22, a43); \ - MOV64(a43, a03); \ - MOV64(a03, a44); \ - MOV64(a44, a31); \ - MOV64(a31, t); \ - MOV64(t, a10); \ - MOV64(a10, a41); \ - MOV64(a41, a42); \ - MOV64(a42, a20); \ - MOV64(a20, a32); \ - MOV64(a32, a34); \ - MOV64(a34, a40); \ - MOV64(a40, a14); \ - MOV64(a14, a13); \ - MOV64(a13, a30); \ - MOV64(a30, a23); \ - MOV64(a23, a21); \ - MOV64(a21, t); \ - } while (0) - -#define P4_TO_P0 do { \ - DECL64(t); \ - MOV64(t, a01); \ - MOV64(a01, a12); \ - MOV64(a12, a11); \ - MOV64(a11, a04); \ - MOV64(a04, a43); \ - MOV64(a43, a44); \ - MOV64(a44, t); \ - MOV64(t, a02); \ - MOV64(a02, a24); \ - MOV64(a24, a22); \ - MOV64(a22, a03); \ - MOV64(a03, a31); \ - MOV64(a31, a33); \ - MOV64(a33, t); \ - MOV64(t, a10); \ - MOV64(a10, a42); \ - MOV64(a42, a32); \ - MOV64(a32, a40); \ - MOV64(a40, a13); \ - MOV64(a13, a23); \ - MOV64(a23, t); \ - MOV64(t, a14); \ - MOV64(a14, a30); \ - MOV64(a30, a21); \ - MOV64(a21, a41); \ - MOV64(a41, a20); \ - MOV64(a20, a34); \ - MOV64(a34, t); \ - } while (0) - -#define P6_TO_P0 do { \ - DECL64(t); \ - MOV64(t, a01); \ - MOV64(a01, a02); \ - MOV64(a02, a04); \ - MOV64(a04, a03); \ - MOV64(a03, t); \ - MOV64(t, a10); \ - MOV64(a10, a20); \ - MOV64(a20, a40); \ - MOV64(a40, a30); \ - MOV64(a30, t); \ - MOV64(t, a11); \ - MOV64(a11, a22); \ - MOV64(a22, a44); \ - MOV64(a44, a33); \ - MOV64(a33, t); \ - MOV64(t, a12); \ - MOV64(a12, a24); \ - MOV64(a24, a43); \ - MOV64(a43, a31); \ - MOV64(a31, t); \ - MOV64(t, a13); \ - MOV64(a13, a21); \ - MOV64(a21, a42); \ - MOV64(a42, a34); \ - MOV64(a34, t); \ - MOV64(t, a14); \ - MOV64(a14, a23); \ - MOV64(a23, a41); \ - MOV64(a41, a32); \ - MOV64(a32, t); \ - } while (0) - -#define P8_TO_P0 do { \ - DECL64(t); \ - MOV64(t, a01); \ - MOV64(a01, a11); \ - MOV64(a11, a43); \ - MOV64(a43, t); \ - MOV64(t, a02); \ - MOV64(a02, a22); \ - MOV64(a22, a31); \ - MOV64(a31, t); \ - MOV64(t, a03); \ - MOV64(a03, a33); \ - MOV64(a33, a24); \ - MOV64(a24, t); \ - MOV64(t, a04); \ - MOV64(a04, a44); \ - MOV64(a44, a12); \ - MOV64(a12, t); \ - MOV64(t, a10); \ - MOV64(a10, a32); \ - MOV64(a32, a13); \ - MOV64(a13, t); \ - MOV64(t, a14); \ - MOV64(a14, a21); \ - MOV64(a21, a20); \ - MOV64(a20, t); \ - MOV64(t, a23); \ - MOV64(a23, a42); \ - MOV64(a42, a40); \ - MOV64(a40, t); \ - MOV64(t, a30); \ - MOV64(a30, a41); \ - MOV64(a41, a34); \ - MOV64(a34, t); \ - } while (0) - -#define P12_TO_P0 do { \ - DECL64(t); \ - MOV64(t, a01); \ - MOV64(a01, a04); \ - MOV64(a04, t); \ - MOV64(t, a02); \ - MOV64(a02, a03); \ - MOV64(a03, t); \ - MOV64(t, a10); \ - MOV64(a10, a40); \ - MOV64(a40, t); \ - MOV64(t, a11); \ - MOV64(a11, a44); \ - MOV64(a44, t); \ - MOV64(t, a12); \ - MOV64(a12, a43); \ - MOV64(a43, t); \ - MOV64(t, a13); \ - MOV64(a13, a42); \ - MOV64(a42, t); \ - MOV64(t, a14); \ - MOV64(a14, a41); \ - MOV64(a41, t); \ - MOV64(t, a20); \ - MOV64(a20, a30); \ - MOV64(a30, t); \ - MOV64(t, a21); \ - MOV64(a21, a34); \ - MOV64(a34, t); \ - MOV64(t, a22); \ - MOV64(a22, a33); \ - MOV64(a33, t); \ - MOV64(t, a23); \ - MOV64(a23, a32); \ - MOV64(a32, t); \ - MOV64(t, a24); \ - MOV64(a24, a31); \ - MOV64(a31, t); \ - } while (0) - -#define LPAR ( -#define RPAR ) - -#define KF_ELT(r, s, k) do { \ - THETA LPAR P ## r RPAR; \ - RHO LPAR P ## r RPAR; \ - KHI LPAR P ## s RPAR; \ - IOTA(k); \ - } while (0) - -#define DO(x) x - -#define KECCAK_F_1600 DO(KECCAK_F_1600_) - -#if SPH_KECCAK_UNROLL == 1 - -#define KECCAK_F_1600_ do { \ - int j; \ - for (j = 0; j < 24; j ++) { \ - KF_ELT( 0, 1, RC[j + 0]); \ - P1_TO_P0; \ - } \ - } while (0) - -#elif SPH_KECCAK_UNROLL == 2 - -#define KECCAK_F_1600_ do { \ - int j; \ - for (j = 0; j < 24; j += 2) { \ - KF_ELT( 0, 1, RC[j + 0]); \ - KF_ELT( 1, 2, RC[j + 1]); \ - P2_TO_P0; \ - } \ - } while (0) - -#elif SPH_KECCAK_UNROLL == 4 - -#define KECCAK_F_1600_ do { \ - int j; \ - for (j = 0; j < 24; j += 4) { \ - KF_ELT( 0, 1, RC[j + 0]); \ - KF_ELT( 1, 2, RC[j + 1]); \ - KF_ELT( 2, 3, RC[j + 2]); \ - KF_ELT( 3, 4, RC[j + 3]); \ - P4_TO_P0; \ - } \ - } while (0) - -#elif SPH_KECCAK_UNROLL == 6 - -#define KECCAK_F_1600_ do { \ - int j; \ - for (j = 0; j < 24; j += 6) { \ - KF_ELT( 0, 1, RC[j + 0]); \ - KF_ELT( 1, 2, RC[j + 1]); \ - KF_ELT( 2, 3, RC[j + 2]); \ - KF_ELT( 3, 4, RC[j + 3]); \ - KF_ELT( 4, 5, RC[j + 4]); \ - KF_ELT( 5, 6, RC[j + 5]); \ - P6_TO_P0; \ - } \ - } while (0) - -#elif SPH_KECCAK_UNROLL == 8 - -#define KECCAK_F_1600_ do { \ - int j; \ - for (j = 0; j < 24; j += 8) { \ - KF_ELT( 0, 1, RC[j + 0]); \ - KF_ELT( 1, 2, RC[j + 1]); \ - KF_ELT( 2, 3, RC[j + 2]); \ - KF_ELT( 3, 4, RC[j + 3]); \ - KF_ELT( 4, 5, RC[j + 4]); \ - KF_ELT( 5, 6, RC[j + 5]); \ - KF_ELT( 6, 7, RC[j + 6]); \ - KF_ELT( 7, 8, RC[j + 7]); \ - P8_TO_P0; \ - } \ - } while (0) - -#elif SPH_KECCAK_UNROLL == 12 - -#define KECCAK_F_1600_ do { \ - int j; \ - for (j = 0; j < 24; j += 12) { \ - KF_ELT( 0, 1, RC[j + 0]); \ - KF_ELT( 1, 2, RC[j + 1]); \ - KF_ELT( 2, 3, RC[j + 2]); \ - KF_ELT( 3, 4, RC[j + 3]); \ - KF_ELT( 4, 5, RC[j + 4]); \ - KF_ELT( 5, 6, RC[j + 5]); \ - KF_ELT( 6, 7, RC[j + 6]); \ - KF_ELT( 7, 8, RC[j + 7]); \ - KF_ELT( 8, 9, RC[j + 8]); \ - KF_ELT( 9, 10, RC[j + 9]); \ - KF_ELT(10, 11, RC[j + 10]); \ - KF_ELT(11, 12, RC[j + 11]); \ - P12_TO_P0; \ - } \ - } while (0) - -#elif SPH_KECCAK_UNROLL == 0 - -#define KECCAK_F_1600_ do { \ - KF_ELT( 0, 1, RC[ 0]); \ - KF_ELT( 1, 2, RC[ 1]); \ - KF_ELT( 2, 3, RC[ 2]); \ - KF_ELT( 3, 4, RC[ 3]); \ - KF_ELT( 4, 5, RC[ 4]); \ - KF_ELT( 5, 6, RC[ 5]); \ - KF_ELT( 6, 7, RC[ 6]); \ - KF_ELT( 7, 8, RC[ 7]); \ - KF_ELT( 8, 9, RC[ 8]); \ - KF_ELT( 9, 10, RC[ 9]); \ - KF_ELT(10, 11, RC[10]); \ - KF_ELT(11, 12, RC[11]); \ - KF_ELT(12, 13, RC[12]); \ - KF_ELT(13, 14, RC[13]); \ - KF_ELT(14, 15, RC[14]); \ - KF_ELT(15, 16, RC[15]); \ - KF_ELT(16, 17, RC[16]); \ - KF_ELT(17, 18, RC[17]); \ - KF_ELT(18, 19, RC[18]); \ - KF_ELT(19, 20, RC[19]); \ - KF_ELT(20, 21, RC[20]); \ - KF_ELT(21, 22, RC[21]); \ - KF_ELT(22, 23, RC[22]); \ - KF_ELT(23, 0, RC[23]); \ - } while (0) - -#else - -#error Unimplemented unroll count for Keccak. - -#endif - -static void -keccak_init(sph_keccak_context *kc, unsigned out_size) -{ - int i; - -#if SPH_KECCAK_64 - for (i = 0; i < 25; i ++) - kc->u.wide[i] = 0; - /* - * Initialization for the "lane complement". - */ - kc->u.wide[ 1] = SPH_C64(0xFFFFFFFFFFFFFFFF); - kc->u.wide[ 2] = SPH_C64(0xFFFFFFFFFFFFFFFF); - kc->u.wide[ 8] = SPH_C64(0xFFFFFFFFFFFFFFFF); - kc->u.wide[12] = SPH_C64(0xFFFFFFFFFFFFFFFF); - kc->u.wide[17] = SPH_C64(0xFFFFFFFFFFFFFFFF); - kc->u.wide[20] = SPH_C64(0xFFFFFFFFFFFFFFFF); -#else - - for (i = 0; i < 50; i ++) - kc->u.narrow[i] = 0; - /* - * Initialization for the "lane complement". - * Note: since we set to all-one full 64-bit words, - * interleaving (if applicable) is a no-op. - */ - kc->u.narrow[ 2] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[ 3] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[ 4] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[ 5] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[16] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[17] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[24] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[25] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[34] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[35] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[40] = SPH_C32(0xFFFFFFFF); - kc->u.narrow[41] = SPH_C32(0xFFFFFFFF); -#endif - kc->ptr = 0; - kc->lim = 200 - (out_size >> 2); -} - -static void -keccak_core(sph_keccak_context *kc, const void *data, size_t len, size_t lim) -{ - unsigned char *buf; - size_t ptr; - DECL_STATE - - buf = kc->buf; - ptr = kc->ptr; - - if (len < (lim - ptr)) { - memcpy(buf + ptr, data, len); - kc->ptr = ptr + len; - return; - } - - READ_STATE(kc); - while (len > 0) { - size_t clen; - - clen = (lim - ptr); - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - ptr += clen; - data = (const unsigned char *)data + clen; - len -= clen; - if (ptr == lim) { - INPUT_BUF(lim); - KECCAK_F_1600; - ptr = 0; - } - } - WRITE_STATE(kc); - kc->ptr = ptr; -} - -#if SPH_KECCAK_64 - -#define DEFCLOSE(d, lim) \ - static void keccak_close ## d( \ - sph_keccak_context *kc, unsigned ub, unsigned n, void *dst) \ - { \ - unsigned eb; \ - union { \ - unsigned char tmp[lim + 1]; \ - sph_u64 dummy; /* for alignment */ \ - } u; \ - size_t j; \ - \ - eb = (0x100 | (ub & 0xFF)) >> (8 - n); \ - if (kc->ptr == (lim - 1)) { \ - if (n == 7) { \ - u.tmp[0] = eb; \ - memset(u.tmp + 1, 0, lim - 1); \ - u.tmp[lim] = 0x80; \ - j = 1 + lim; \ - } else { \ - u.tmp[0] = eb | 0x80; \ - j = 1; \ - } \ - } else { \ - j = lim - kc->ptr; \ - u.tmp[0] = eb; \ - memset(u.tmp + 1, 0, j - 2); \ - u.tmp[j - 1] = 0x80; \ - } \ - keccak_core(kc, u.tmp, j, lim); \ - /* Finalize the "lane complement" */ \ - kc->u.wide[ 1] = ~kc->u.wide[ 1]; \ - kc->u.wide[ 2] = ~kc->u.wide[ 2]; \ - kc->u.wide[ 8] = ~kc->u.wide[ 8]; \ - kc->u.wide[12] = ~kc->u.wide[12]; \ - kc->u.wide[17] = ~kc->u.wide[17]; \ - kc->u.wide[20] = ~kc->u.wide[20]; \ - for (j = 0; j < d; j += 8) \ - sph_enc64le_aligned(u.tmp + j, kc->u.wide[j >> 3]); \ - memcpy(dst, u.tmp, d); \ - keccak_init(kc, (unsigned)d << 3); \ - } \ - -#else - -#define DEFCLOSE(d, lim) \ - static void keccak_close ## d( \ - sph_keccak_context *kc, unsigned ub, unsigned n, void *dst) \ - { \ - unsigned eb; \ - union { \ - unsigned char tmp[lim + 1]; \ - sph_u64 dummy; /* for alignment */ \ - } u; \ - size_t j; \ - \ - eb = (0x100 | (ub & 0xFF)) >> (8 - n); \ - if (kc->ptr == (lim - 1)) { \ - if (n == 7) { \ - u.tmp[0] = eb; \ - memset(u.tmp + 1, 0, lim - 1); \ - u.tmp[lim] = 0x80; \ - j = 1 + lim; \ - } else { \ - u.tmp[0] = eb | 0x80; \ - j = 1; \ - } \ - } else { \ - j = lim - kc->ptr; \ - u.tmp[0] = eb; \ - memset(u.tmp + 1, 0, j - 2); \ - u.tmp[j - 1] = 0x80; \ - } \ - keccak_core(kc, u.tmp, j, lim); \ - /* Finalize the "lane complement" */ \ - kc->u.narrow[ 2] = ~kc->u.narrow[ 2]; \ - kc->u.narrow[ 3] = ~kc->u.narrow[ 3]; \ - kc->u.narrow[ 4] = ~kc->u.narrow[ 4]; \ - kc->u.narrow[ 5] = ~kc->u.narrow[ 5]; \ - kc->u.narrow[16] = ~kc->u.narrow[16]; \ - kc->u.narrow[17] = ~kc->u.narrow[17]; \ - kc->u.narrow[24] = ~kc->u.narrow[24]; \ - kc->u.narrow[25] = ~kc->u.narrow[25]; \ - kc->u.narrow[34] = ~kc->u.narrow[34]; \ - kc->u.narrow[35] = ~kc->u.narrow[35]; \ - kc->u.narrow[40] = ~kc->u.narrow[40]; \ - kc->u.narrow[41] = ~kc->u.narrow[41]; \ - /* un-interleave */ \ - for (j = 0; j < 50; j += 2) \ - UNINTERLEAVE(kc->u.narrow[j], kc->u.narrow[j + 1]); \ - for (j = 0; j < d; j += 4) \ - sph_enc32le_aligned(u.tmp + j, kc->u.narrow[j >> 2]); \ - memcpy(dst, u.tmp, d); \ - keccak_init(kc, (unsigned)d << 3); \ - } \ - -#endif - -DEFCLOSE(28, 144) -DEFCLOSE(32, 136) -DEFCLOSE(48, 104) -DEFCLOSE(64, 72) - -/* see sph_keccak.h */ -void -sph_keccak224_init(void *cc) -{ - keccak_init(cc, 224); -} - -/* see sph_keccak.h */ -void -sph_keccak224(void *cc, const void *data, size_t len) -{ - keccak_core(cc, data, len, 144); -} - -/* see sph_keccak.h */ -void -sph_keccak224_close(void *cc, void *dst) -{ - sph_keccak224_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_keccak.h */ -void -sph_keccak224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - keccak_close28(cc, ub, n, dst); -} - -/* see sph_keccak.h */ -void -sph_keccak256_init(void *cc) -{ - keccak_init(cc, 256); -} - -/* see sph_keccak.h */ -void -sph_keccak256(void *cc, const void *data, size_t len) -{ - keccak_core(cc, data, len, 136); -} - -/* see sph_keccak.h */ -void -sph_keccak256_close(void *cc, void *dst) -{ - sph_keccak256_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_keccak.h */ -void -sph_keccak256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - keccak_close32(cc, ub, n, dst); -} - -/* see sph_keccak.h */ -void -sph_keccak384_init(void *cc) -{ - keccak_init(cc, 384); -} - -/* see sph_keccak.h */ -void -sph_keccak384(void *cc, const void *data, size_t len) -{ - keccak_core(cc, data, len, 104); -} - -/* see sph_keccak.h */ -void -sph_keccak384_close(void *cc, void *dst) -{ - sph_keccak384_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_keccak.h */ -void -sph_keccak384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - keccak_close48(cc, ub, n, dst); -} - -/* see sph_keccak.h */ -void -sph_keccak512_init(void *cc) -{ - keccak_init(cc, 512); -} - -/* see sph_keccak.h */ -void -sph_keccak512(void *cc, const void *data, size_t len) -{ - keccak_core(cc, data, len, 72); -} - -/* see sph_keccak.h */ -void -sph_keccak512_close(void *cc, void *dst) -{ - sph_keccak512_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_keccak.h */ -void -sph_keccak512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - keccak_close64(cc, ub, n, dst); -} - - -#ifdef __cplusplus -} -#endif diff --git a/src/crypto/skein.c b/src/crypto/skein.c deleted file mode 100644 index 7e47e352211b..000000000000 --- a/src/crypto/skein.c +++ /dev/null @@ -1,1254 +0,0 @@ -/* $Id: skein.c 254 2011-06-07 19:38:58Z tp $ */ -/* - * Skein implementation. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @author Thomas Pornin - */ - -#include -#include - -#include "sph_skein.h" - -#ifdef __cplusplus -extern "C"{ -#endif - - -#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_SKEIN -#define SPH_SMALL_FOOTPRINT_SKEIN 1 -#endif - -#ifdef _MSC_VER -#pragma warning (disable: 4146) -#endif - -#if SPH_64 - -#if 0 -/* obsolete */ -/* - * M5_ ## s ## _ ## i evaluates to s+i mod 5 (0 <= s <= 18, 0 <= i <= 3). - */ - -#define M5_0_0 0 -#define M5_0_1 1 -#define M5_0_2 2 -#define M5_0_3 3 - -#define M5_1_0 1 -#define M5_1_1 2 -#define M5_1_2 3 -#define M5_1_3 4 - -#define M5_2_0 2 -#define M5_2_1 3 -#define M5_2_2 4 -#define M5_2_3 0 - -#define M5_3_0 3 -#define M5_3_1 4 -#define M5_3_2 0 -#define M5_3_3 1 - -#define M5_4_0 4 -#define M5_4_1 0 -#define M5_4_2 1 -#define M5_4_3 2 - -#define M5_5_0 0 -#define M5_5_1 1 -#define M5_5_2 2 -#define M5_5_3 3 - -#define M5_6_0 1 -#define M5_6_1 2 -#define M5_6_2 3 -#define M5_6_3 4 - -#define M5_7_0 2 -#define M5_7_1 3 -#define M5_7_2 4 -#define M5_7_3 0 - -#define M5_8_0 3 -#define M5_8_1 4 -#define M5_8_2 0 -#define M5_8_3 1 - -#define M5_9_0 4 -#define M5_9_1 0 -#define M5_9_2 1 -#define M5_9_3 2 - -#define M5_10_0 0 -#define M5_10_1 1 -#define M5_10_2 2 -#define M5_10_3 3 - -#define M5_11_0 1 -#define M5_11_1 2 -#define M5_11_2 3 -#define M5_11_3 4 - -#define M5_12_0 2 -#define M5_12_1 3 -#define M5_12_2 4 -#define M5_12_3 0 - -#define M5_13_0 3 -#define M5_13_1 4 -#define M5_13_2 0 -#define M5_13_3 1 - -#define M5_14_0 4 -#define M5_14_1 0 -#define M5_14_2 1 -#define M5_14_3 2 - -#define M5_15_0 0 -#define M5_15_1 1 -#define M5_15_2 2 -#define M5_15_3 3 - -#define M5_16_0 1 -#define M5_16_1 2 -#define M5_16_2 3 -#define M5_16_3 4 - -#define M5_17_0 2 -#define M5_17_1 3 -#define M5_17_2 4 -#define M5_17_3 0 - -#define M5_18_0 3 -#define M5_18_1 4 -#define M5_18_2 0 -#define M5_18_3 1 -#endif - -/* - * M9_ ## s ## _ ## i evaluates to s+i mod 9 (0 <= s <= 18, 0 <= i <= 7). - */ - -#define M9_0_0 0 -#define M9_0_1 1 -#define M9_0_2 2 -#define M9_0_3 3 -#define M9_0_4 4 -#define M9_0_5 5 -#define M9_0_6 6 -#define M9_0_7 7 - -#define M9_1_0 1 -#define M9_1_1 2 -#define M9_1_2 3 -#define M9_1_3 4 -#define M9_1_4 5 -#define M9_1_5 6 -#define M9_1_6 7 -#define M9_1_7 8 - -#define M9_2_0 2 -#define M9_2_1 3 -#define M9_2_2 4 -#define M9_2_3 5 -#define M9_2_4 6 -#define M9_2_5 7 -#define M9_2_6 8 -#define M9_2_7 0 - -#define M9_3_0 3 -#define M9_3_1 4 -#define M9_3_2 5 -#define M9_3_3 6 -#define M9_3_4 7 -#define M9_3_5 8 -#define M9_3_6 0 -#define M9_3_7 1 - -#define M9_4_0 4 -#define M9_4_1 5 -#define M9_4_2 6 -#define M9_4_3 7 -#define M9_4_4 8 -#define M9_4_5 0 -#define M9_4_6 1 -#define M9_4_7 2 - -#define M9_5_0 5 -#define M9_5_1 6 -#define M9_5_2 7 -#define M9_5_3 8 -#define M9_5_4 0 -#define M9_5_5 1 -#define M9_5_6 2 -#define M9_5_7 3 - -#define M9_6_0 6 -#define M9_6_1 7 -#define M9_6_2 8 -#define M9_6_3 0 -#define M9_6_4 1 -#define M9_6_5 2 -#define M9_6_6 3 -#define M9_6_7 4 - -#define M9_7_0 7 -#define M9_7_1 8 -#define M9_7_2 0 -#define M9_7_3 1 -#define M9_7_4 2 -#define M9_7_5 3 -#define M9_7_6 4 -#define M9_7_7 5 - -#define M9_8_0 8 -#define M9_8_1 0 -#define M9_8_2 1 -#define M9_8_3 2 -#define M9_8_4 3 -#define M9_8_5 4 -#define M9_8_6 5 -#define M9_8_7 6 - -#define M9_9_0 0 -#define M9_9_1 1 -#define M9_9_2 2 -#define M9_9_3 3 -#define M9_9_4 4 -#define M9_9_5 5 -#define M9_9_6 6 -#define M9_9_7 7 - -#define M9_10_0 1 -#define M9_10_1 2 -#define M9_10_2 3 -#define M9_10_3 4 -#define M9_10_4 5 -#define M9_10_5 6 -#define M9_10_6 7 -#define M9_10_7 8 - -#define M9_11_0 2 -#define M9_11_1 3 -#define M9_11_2 4 -#define M9_11_3 5 -#define M9_11_4 6 -#define M9_11_5 7 -#define M9_11_6 8 -#define M9_11_7 0 - -#define M9_12_0 3 -#define M9_12_1 4 -#define M9_12_2 5 -#define M9_12_3 6 -#define M9_12_4 7 -#define M9_12_5 8 -#define M9_12_6 0 -#define M9_12_7 1 - -#define M9_13_0 4 -#define M9_13_1 5 -#define M9_13_2 6 -#define M9_13_3 7 -#define M9_13_4 8 -#define M9_13_5 0 -#define M9_13_6 1 -#define M9_13_7 2 - -#define M9_14_0 5 -#define M9_14_1 6 -#define M9_14_2 7 -#define M9_14_3 8 -#define M9_14_4 0 -#define M9_14_5 1 -#define M9_14_6 2 -#define M9_14_7 3 - -#define M9_15_0 6 -#define M9_15_1 7 -#define M9_15_2 8 -#define M9_15_3 0 -#define M9_15_4 1 -#define M9_15_5 2 -#define M9_15_6 3 -#define M9_15_7 4 - -#define M9_16_0 7 -#define M9_16_1 8 -#define M9_16_2 0 -#define M9_16_3 1 -#define M9_16_4 2 -#define M9_16_5 3 -#define M9_16_6 4 -#define M9_16_7 5 - -#define M9_17_0 8 -#define M9_17_1 0 -#define M9_17_2 1 -#define M9_17_3 2 -#define M9_17_4 3 -#define M9_17_5 4 -#define M9_17_6 5 -#define M9_17_7 6 - -#define M9_18_0 0 -#define M9_18_1 1 -#define M9_18_2 2 -#define M9_18_3 3 -#define M9_18_4 4 -#define M9_18_5 5 -#define M9_18_6 6 -#define M9_18_7 7 - -/* - * M3_ ## s ## _ ## i evaluates to s+i mod 3 (0 <= s <= 18, 0 <= i <= 1). - */ - -#define M3_0_0 0 -#define M3_0_1 1 -#define M3_1_0 1 -#define M3_1_1 2 -#define M3_2_0 2 -#define M3_2_1 0 -#define M3_3_0 0 -#define M3_3_1 1 -#define M3_4_0 1 -#define M3_4_1 2 -#define M3_5_0 2 -#define M3_5_1 0 -#define M3_6_0 0 -#define M3_6_1 1 -#define M3_7_0 1 -#define M3_7_1 2 -#define M3_8_0 2 -#define M3_8_1 0 -#define M3_9_0 0 -#define M3_9_1 1 -#define M3_10_0 1 -#define M3_10_1 2 -#define M3_11_0 2 -#define M3_11_1 0 -#define M3_12_0 0 -#define M3_12_1 1 -#define M3_13_0 1 -#define M3_13_1 2 -#define M3_14_0 2 -#define M3_14_1 0 -#define M3_15_0 0 -#define M3_15_1 1 -#define M3_16_0 1 -#define M3_16_1 2 -#define M3_17_0 2 -#define M3_17_1 0 -#define M3_18_0 0 -#define M3_18_1 1 - -#define XCAT(x, y) XCAT_(x, y) -#define XCAT_(x, y) x ## y - -#if 0 -/* obsolete */ -#define SKSI(k, s, i) XCAT(k, XCAT(XCAT(XCAT(M5_, s), _), i)) -#define SKST(t, s, v) XCAT(t, XCAT(XCAT(XCAT(M3_, s), _), v)) -#endif - -#define SKBI(k, s, i) XCAT(k, XCAT(XCAT(XCAT(M9_, s), _), i)) -#define SKBT(t, s, v) XCAT(t, XCAT(XCAT(XCAT(M3_, s), _), v)) - -#if 0 -/* obsolete */ -#define TFSMALL_KINIT(k0, k1, k2, k3, k4, t0, t1, t2) do { \ - k4 = (k0 ^ k1) ^ (k2 ^ k3) ^ SPH_C64(0x1BD11BDAA9FC1A22); \ - t2 = t0 ^ t1; \ - } while (0) -#endif - -#define TFBIG_KINIT(k0, k1, k2, k3, k4, k5, k6, k7, k8, t0, t1, t2) do { \ - k8 = ((k0 ^ k1) ^ (k2 ^ k3)) ^ ((k4 ^ k5) ^ (k6 ^ k7)) \ - ^ SPH_C64(0x1BD11BDAA9FC1A22); \ - t2 = t0 ^ t1; \ - } while (0) - -#if 0 -/* obsolete */ -#define TFSMALL_ADDKEY(w0, w1, w2, w3, k, t, s) do { \ - w0 = SPH_T64(w0 + SKSI(k, s, 0)); \ - w1 = SPH_T64(w1 + SKSI(k, s, 1) + SKST(t, s, 0)); \ - w2 = SPH_T64(w2 + SKSI(k, s, 2) + SKST(t, s, 1)); \ - w3 = SPH_T64(w3 + SKSI(k, s, 3) + (sph_u64)s); \ - } while (0) -#endif - -#if SPH_SMALL_FOOTPRINT_SKEIN - -#define TFBIG_ADDKEY(s, tt0, tt1) do { \ - p0 = SPH_T64(p0 + h[s + 0]); \ - p1 = SPH_T64(p1 + h[s + 1]); \ - p2 = SPH_T64(p2 + h[s + 2]); \ - p3 = SPH_T64(p3 + h[s + 3]); \ - p4 = SPH_T64(p4 + h[s + 4]); \ - p5 = SPH_T64(p5 + h[s + 5] + tt0); \ - p6 = SPH_T64(p6 + h[s + 6] + tt1); \ - p7 = SPH_T64(p7 + h[s + 7] + (sph_u64)s); \ - } while (0) - -#else - -#define TFBIG_ADDKEY(w0, w1, w2, w3, w4, w5, w6, w7, k, t, s) do { \ - w0 = SPH_T64(w0 + SKBI(k, s, 0)); \ - w1 = SPH_T64(w1 + SKBI(k, s, 1)); \ - w2 = SPH_T64(w2 + SKBI(k, s, 2)); \ - w3 = SPH_T64(w3 + SKBI(k, s, 3)); \ - w4 = SPH_T64(w4 + SKBI(k, s, 4)); \ - w5 = SPH_T64(w5 + SKBI(k, s, 5) + SKBT(t, s, 0)); \ - w6 = SPH_T64(w6 + SKBI(k, s, 6) + SKBT(t, s, 1)); \ - w7 = SPH_T64(w7 + SKBI(k, s, 7) + (sph_u64)s); \ - } while (0) - -#endif - -#if 0 -/* obsolete */ -#define TFSMALL_MIX(x0, x1, rc) do { \ - x0 = SPH_T64(x0 + x1); \ - x1 = SPH_ROTL64(x1, rc) ^ x0; \ - } while (0) -#endif - -#define TFBIG_MIX(x0, x1, rc) do { \ - x0 = SPH_T64(x0 + x1); \ - x1 = SPH_ROTL64(x1, rc) ^ x0; \ - } while (0) - -#if 0 -/* obsolete */ -#define TFSMALL_MIX4(w0, w1, w2, w3, rc0, rc1) do { \ - TFSMALL_MIX(w0, w1, rc0); \ - TFSMALL_MIX(w2, w3, rc1); \ - } while (0) -#endif - -#define TFBIG_MIX8(w0, w1, w2, w3, w4, w5, w6, w7, rc0, rc1, rc2, rc3) do { \ - TFBIG_MIX(w0, w1, rc0); \ - TFBIG_MIX(w2, w3, rc1); \ - TFBIG_MIX(w4, w5, rc2); \ - TFBIG_MIX(w6, w7, rc3); \ - } while (0) - -#if 0 -/* obsolete */ -#define TFSMALL_4e(s) do { \ - TFSMALL_ADDKEY(p0, p1, p2, p3, h, t, s); \ - TFSMALL_MIX4(p0, p1, p2, p3, 14, 16); \ - TFSMALL_MIX4(p0, p3, p2, p1, 52, 57); \ - TFSMALL_MIX4(p0, p1, p2, p3, 23, 40); \ - TFSMALL_MIX4(p0, p3, p2, p1, 5, 37); \ - } while (0) - -#define TFSMALL_4o(s) do { \ - TFSMALL_ADDKEY(p0, p1, p2, p3, h, t, s); \ - TFSMALL_MIX4(p0, p1, p2, p3, 25, 33); \ - TFSMALL_MIX4(p0, p3, p2, p1, 46, 12); \ - TFSMALL_MIX4(p0, p1, p2, p3, 58, 22); \ - TFSMALL_MIX4(p0, p3, p2, p1, 32, 32); \ - } while (0) -#endif - -#if SPH_SMALL_FOOTPRINT_SKEIN - -#define TFBIG_4e(s) do { \ - TFBIG_ADDKEY(s, t0, t1); \ - TFBIG_MIX8(p0, p1, p2, p3, p4, p5, p6, p7, 46, 36, 19, 37); \ - TFBIG_MIX8(p2, p1, p4, p7, p6, p5, p0, p3, 33, 27, 14, 42); \ - TFBIG_MIX8(p4, p1, p6, p3, p0, p5, p2, p7, 17, 49, 36, 39); \ - TFBIG_MIX8(p6, p1, p0, p7, p2, p5, p4, p3, 44, 9, 54, 56); \ - } while (0) - -#define TFBIG_4o(s) do { \ - TFBIG_ADDKEY(s, t1, t2); \ - TFBIG_MIX8(p0, p1, p2, p3, p4, p5, p6, p7, 39, 30, 34, 24); \ - TFBIG_MIX8(p2, p1, p4, p7, p6, p5, p0, p3, 13, 50, 10, 17); \ - TFBIG_MIX8(p4, p1, p6, p3, p0, p5, p2, p7, 25, 29, 39, 43); \ - TFBIG_MIX8(p6, p1, p0, p7, p2, p5, p4, p3, 8, 35, 56, 22); \ - } while (0) - -#else - -#define TFBIG_4e(s) do { \ - TFBIG_ADDKEY(p0, p1, p2, p3, p4, p5, p6, p7, h, t, s); \ - TFBIG_MIX8(p0, p1, p2, p3, p4, p5, p6, p7, 46, 36, 19, 37); \ - TFBIG_MIX8(p2, p1, p4, p7, p6, p5, p0, p3, 33, 27, 14, 42); \ - TFBIG_MIX8(p4, p1, p6, p3, p0, p5, p2, p7, 17, 49, 36, 39); \ - TFBIG_MIX8(p6, p1, p0, p7, p2, p5, p4, p3, 44, 9, 54, 56); \ - } while (0) - -#define TFBIG_4o(s) do { \ - TFBIG_ADDKEY(p0, p1, p2, p3, p4, p5, p6, p7, h, t, s); \ - TFBIG_MIX8(p0, p1, p2, p3, p4, p5, p6, p7, 39, 30, 34, 24); \ - TFBIG_MIX8(p2, p1, p4, p7, p6, p5, p0, p3, 13, 50, 10, 17); \ - TFBIG_MIX8(p4, p1, p6, p3, p0, p5, p2, p7, 25, 29, 39, 43); \ - TFBIG_MIX8(p6, p1, p0, p7, p2, p5, p4, p3, 8, 35, 56, 22); \ - } while (0) - -#endif - -#if 0 -/* obsolete */ -#define UBI_SMALL(etype, extra) do { \ - sph_u64 h4, t0, t1, t2; \ - sph_u64 m0 = sph_dec64le(buf + 0); \ - sph_u64 m1 = sph_dec64le(buf + 8); \ - sph_u64 m2 = sph_dec64le(buf + 16); \ - sph_u64 m3 = sph_dec64le(buf + 24); \ - sph_u64 p0 = m0; \ - sph_u64 p1 = m1; \ - sph_u64 p2 = m2; \ - sph_u64 p3 = m3; \ - t0 = SPH_T64(bcount << 5) + (sph_u64)(extra); \ - t1 = (bcount >> 59) + ((sph_u64)(etype) << 55); \ - TFSMALL_KINIT(h0, h1, h2, h3, h4, t0, t1, t2); \ - TFSMALL_4e(0); \ - TFSMALL_4o(1); \ - TFSMALL_4e(2); \ - TFSMALL_4o(3); \ - TFSMALL_4e(4); \ - TFSMALL_4o(5); \ - TFSMALL_4e(6); \ - TFSMALL_4o(7); \ - TFSMALL_4e(8); \ - TFSMALL_4o(9); \ - TFSMALL_4e(10); \ - TFSMALL_4o(11); \ - TFSMALL_4e(12); \ - TFSMALL_4o(13); \ - TFSMALL_4e(14); \ - TFSMALL_4o(15); \ - TFSMALL_4e(16); \ - TFSMALL_4o(17); \ - TFSMALL_ADDKEY(p0, p1, p2, p3, h, t, 18); \ - h0 = m0 ^ p0; \ - h1 = m1 ^ p1; \ - h2 = m2 ^ p2; \ - h3 = m3 ^ p3; \ - } while (0) -#endif - -#if SPH_SMALL_FOOTPRINT_SKEIN - -#define UBI_BIG(etype, extra) do { \ - sph_u64 t0, t1, t2; \ - unsigned u; \ - sph_u64 m0 = sph_dec64le_aligned(buf + 0); \ - sph_u64 m1 = sph_dec64le_aligned(buf + 8); \ - sph_u64 m2 = sph_dec64le_aligned(buf + 16); \ - sph_u64 m3 = sph_dec64le_aligned(buf + 24); \ - sph_u64 m4 = sph_dec64le_aligned(buf + 32); \ - sph_u64 m5 = sph_dec64le_aligned(buf + 40); \ - sph_u64 m6 = sph_dec64le_aligned(buf + 48); \ - sph_u64 m7 = sph_dec64le_aligned(buf + 56); \ - sph_u64 p0 = m0; \ - sph_u64 p1 = m1; \ - sph_u64 p2 = m2; \ - sph_u64 p3 = m3; \ - sph_u64 p4 = m4; \ - sph_u64 p5 = m5; \ - sph_u64 p6 = m6; \ - sph_u64 p7 = m7; \ - t0 = SPH_T64(bcount << 6) + (sph_u64)(extra); \ - t1 = (bcount >> 58) + ((sph_u64)(etype) << 55); \ - TFBIG_KINIT(h[0], h[1], h[2], h[3], h[4], h[5], \ - h[6], h[7], h[8], t0, t1, t2); \ - for (u = 0; u <= 15; u += 3) { \ - h[u + 9] = h[u + 0]; \ - h[u + 10] = h[u + 1]; \ - h[u + 11] = h[u + 2]; \ - } \ - for (u = 0; u < 9; u ++) { \ - sph_u64 s = u << 1; \ - sph_u64 tmp; \ - TFBIG_4e(s); \ - TFBIG_4o(s + 1); \ - tmp = t2; \ - t2 = t1; \ - t1 = t0; \ - t0 = tmp; \ - } \ - TFBIG_ADDKEY(18, t0, t1); \ - h[0] = m0 ^ p0; \ - h[1] = m1 ^ p1; \ - h[2] = m2 ^ p2; \ - h[3] = m3 ^ p3; \ - h[4] = m4 ^ p4; \ - h[5] = m5 ^ p5; \ - h[6] = m6 ^ p6; \ - h[7] = m7 ^ p7; \ - } while (0) - -#else - -#define UBI_BIG(etype, extra) do { \ - sph_u64 h8, t0, t1, t2; \ - sph_u64 m0 = sph_dec64le_aligned(buf + 0); \ - sph_u64 m1 = sph_dec64le_aligned(buf + 8); \ - sph_u64 m2 = sph_dec64le_aligned(buf + 16); \ - sph_u64 m3 = sph_dec64le_aligned(buf + 24); \ - sph_u64 m4 = sph_dec64le_aligned(buf + 32); \ - sph_u64 m5 = sph_dec64le_aligned(buf + 40); \ - sph_u64 m6 = sph_dec64le_aligned(buf + 48); \ - sph_u64 m7 = sph_dec64le_aligned(buf + 56); \ - sph_u64 p0 = m0; \ - sph_u64 p1 = m1; \ - sph_u64 p2 = m2; \ - sph_u64 p3 = m3; \ - sph_u64 p4 = m4; \ - sph_u64 p5 = m5; \ - sph_u64 p6 = m6; \ - sph_u64 p7 = m7; \ - t0 = SPH_T64(bcount << 6) + (sph_u64)(extra); \ - t1 = (bcount >> 58) + ((sph_u64)(etype) << 55); \ - TFBIG_KINIT(h0, h1, h2, h3, h4, h5, h6, h7, h8, t0, t1, t2); \ - TFBIG_4e(0); \ - TFBIG_4o(1); \ - TFBIG_4e(2); \ - TFBIG_4o(3); \ - TFBIG_4e(4); \ - TFBIG_4o(5); \ - TFBIG_4e(6); \ - TFBIG_4o(7); \ - TFBIG_4e(8); \ - TFBIG_4o(9); \ - TFBIG_4e(10); \ - TFBIG_4o(11); \ - TFBIG_4e(12); \ - TFBIG_4o(13); \ - TFBIG_4e(14); \ - TFBIG_4o(15); \ - TFBIG_4e(16); \ - TFBIG_4o(17); \ - TFBIG_ADDKEY(p0, p1, p2, p3, p4, p5, p6, p7, h, t, 18); \ - h0 = m0 ^ p0; \ - h1 = m1 ^ p1; \ - h2 = m2 ^ p2; \ - h3 = m3 ^ p3; \ - h4 = m4 ^ p4; \ - h5 = m5 ^ p5; \ - h6 = m6 ^ p6; \ - h7 = m7 ^ p7; \ - } while (0) - -#endif - -#if 0 -/* obsolete */ -#define DECL_STATE_SMALL \ - sph_u64 h0, h1, h2, h3; \ - sph_u64 bcount; - -#define READ_STATE_SMALL(sc) do { \ - h0 = (sc)->h0; \ - h1 = (sc)->h1; \ - h2 = (sc)->h2; \ - h3 = (sc)->h3; \ - bcount = sc->bcount; \ - } while (0) - -#define WRITE_STATE_SMALL(sc) do { \ - (sc)->h0 = h0; \ - (sc)->h1 = h1; \ - (sc)->h2 = h2; \ - (sc)->h3 = h3; \ - sc->bcount = bcount; \ - } while (0) -#endif - -#if SPH_SMALL_FOOTPRINT_SKEIN - -#define DECL_STATE_BIG \ - sph_u64 h[27]; \ - sph_u64 bcount; - -#define READ_STATE_BIG(sc) do { \ - h[0] = (sc)->h0; \ - h[1] = (sc)->h1; \ - h[2] = (sc)->h2; \ - h[3] = (sc)->h3; \ - h[4] = (sc)->h4; \ - h[5] = (sc)->h5; \ - h[6] = (sc)->h6; \ - h[7] = (sc)->h7; \ - bcount = sc->bcount; \ - } while (0) - -#define WRITE_STATE_BIG(sc) do { \ - (sc)->h0 = h[0]; \ - (sc)->h1 = h[1]; \ - (sc)->h2 = h[2]; \ - (sc)->h3 = h[3]; \ - (sc)->h4 = h[4]; \ - (sc)->h5 = h[5]; \ - (sc)->h6 = h[6]; \ - (sc)->h7 = h[7]; \ - sc->bcount = bcount; \ - } while (0) - -#else - -#define DECL_STATE_BIG \ - sph_u64 h0, h1, h2, h3, h4, h5, h6, h7; \ - sph_u64 bcount; - -#define READ_STATE_BIG(sc) do { \ - h0 = (sc)->h0; \ - h1 = (sc)->h1; \ - h2 = (sc)->h2; \ - h3 = (sc)->h3; \ - h4 = (sc)->h4; \ - h5 = (sc)->h5; \ - h6 = (sc)->h6; \ - h7 = (sc)->h7; \ - bcount = sc->bcount; \ - } while (0) - -#define WRITE_STATE_BIG(sc) do { \ - (sc)->h0 = h0; \ - (sc)->h1 = h1; \ - (sc)->h2 = h2; \ - (sc)->h3 = h3; \ - (sc)->h4 = h4; \ - (sc)->h5 = h5; \ - (sc)->h6 = h6; \ - (sc)->h7 = h7; \ - sc->bcount = bcount; \ - } while (0) - -#endif - -#if 0 -/* obsolete */ -static void -skein_small_init(sph_skein_small_context *sc, const sph_u64 *iv) -{ - sc->h0 = iv[0]; - sc->h1 = iv[1]; - sc->h2 = iv[2]; - sc->h3 = iv[3]; - sc->bcount = 0; - sc->ptr = 0; -} -#endif - -static void -skein_big_init(sph_skein_big_context *sc, const sph_u64 *iv) -{ - sc->h0 = iv[0]; - sc->h1 = iv[1]; - sc->h2 = iv[2]; - sc->h3 = iv[3]; - sc->h4 = iv[4]; - sc->h5 = iv[5]; - sc->h6 = iv[6]; - sc->h7 = iv[7]; - sc->bcount = 0; - sc->ptr = 0; -} - -#if 0 -/* obsolete */ -static void -skein_small_core(sph_skein_small_context *sc, const void *data, size_t len) -{ - unsigned char *buf; - size_t ptr, clen; - unsigned first; - DECL_STATE_SMALL - - buf = sc->buf; - ptr = sc->ptr; - clen = (sizeof sc->buf) - ptr; - if (len <= clen) { - memcpy(buf + ptr, data, len); - sc->ptr = ptr + len; - return; - } - if (clen != 0) { - memcpy(buf + ptr, data, clen); - data = (const unsigned char *)data + clen; - len -= clen; - } - -#if SPH_SMALL_FOOTPRINT_SKEIN - - READ_STATE_SMALL(sc); - first = (bcount == 0) << 7; - for (;;) { - bcount ++; - UBI_SMALL(96 + first, 0); - if (len <= sizeof sc->buf) - break; - first = 0; - memcpy(buf, data, sizeof sc->buf); - data = (const unsigned char *)data + sizeof sc->buf; - len -= sizeof sc->buf; - } - WRITE_STATE_SMALL(sc); - sc->ptr = len; - memcpy(buf, data, len); - -#else - - /* - * Unrolling the loop yields a slight performance boost, while - * keeping the code size aorund 24 kB on 32-bit x86. - */ - READ_STATE_SMALL(sc); - first = (bcount == 0) << 7; - for (;;) { - bcount ++; - UBI_SMALL(96 + first, 0); - if (len <= sizeof sc->buf) - break; - buf = (unsigned char *)data; - bcount ++; - UBI_SMALL(96, 0); - if (len <= 2 * sizeof sc->buf) { - data = buf + sizeof sc->buf; - len -= sizeof sc->buf; - break; - } - buf += sizeof sc->buf; - data = buf + sizeof sc->buf; - first = 0; - len -= 2 * sizeof sc->buf; - } - WRITE_STATE_SMALL(sc); - sc->ptr = len; - memcpy(sc->buf, data, len); - -#endif -} -#endif - -static void -skein_big_core(sph_skein_big_context *sc, const void *data, size_t len) -{ - /* - * The Skein "final bit" in the tweak is troublesome here, - * because if the input has a length which is a multiple of the - * block size (512 bits) then that bit must be set for the - * final block, which is full of message bits (padding in - * Skein can be reduced to no extra bit at all). However, this - * function cannot know whether it processes the last chunks of - * the message or not. Hence we may keep a full block of buffered - * data (64 bytes). - */ - unsigned char *buf; - size_t ptr; - unsigned first; - DECL_STATE_BIG - - buf = sc->buf; - ptr = sc->ptr; - if (len <= (sizeof sc->buf) - ptr) { - memcpy(buf + ptr, data, len); - ptr += len; - sc->ptr = ptr; - return; - } - - READ_STATE_BIG(sc); - first = (bcount == 0) << 7; - do { - size_t clen; - - if (ptr == sizeof sc->buf) { - bcount ++; - UBI_BIG(96 + first, 0); - first = 0; - ptr = 0; - } - clen = (sizeof sc->buf) - ptr; - if (clen > len) - clen = len; - memcpy(buf + ptr, data, clen); - ptr += clen; - data = (const unsigned char *)data + clen; - len -= clen; - } while (len > 0); - WRITE_STATE_BIG(sc); - sc->ptr = ptr; -} - -#if 0 -/* obsolete */ -static void -skein_small_close(sph_skein_small_context *sc, unsigned ub, unsigned n, - void *dst, size_t out_len) -{ - unsigned char *buf; - size_t ptr; - unsigned et; - int i; - DECL_STATE_SMALL - - if (n != 0) { - unsigned z; - unsigned char x; - - z = 0x80 >> n; - x = ((ub & -z) | z) & 0xFF; - skein_small_core(sc, &x, 1); - } - - buf = sc->buf; - ptr = sc->ptr; - READ_STATE_SMALL(sc); - memset(buf + ptr, 0, (sizeof sc->buf) - ptr); - et = 352 + ((bcount == 0) << 7) + (n != 0); - for (i = 0; i < 2; i ++) { - UBI_SMALL(et, ptr); - if (i == 0) { - memset(buf, 0, sizeof sc->buf); - bcount = 0; - et = 510; - ptr = 8; - } - } - - sph_enc64le_aligned(buf + 0, h0); - sph_enc64le_aligned(buf + 8, h1); - sph_enc64le_aligned(buf + 16, h2); - sph_enc64le_aligned(buf + 24, h3); - memcpy(dst, buf, out_len); -} -#endif - -static void -skein_big_close(sph_skein_big_context *sc, unsigned ub, unsigned n, - void *dst, size_t out_len) -{ - unsigned char *buf; - size_t ptr; - unsigned et; - int i; -#if SPH_SMALL_FOOTPRINT_SKEIN - size_t u; -#endif - DECL_STATE_BIG - - /* - * Add bit padding if necessary. - */ - if (n != 0) { - unsigned z; - unsigned char x; - - z = 0x80 >> n; - x = ((ub & -z) | z) & 0xFF; - skein_big_core(sc, &x, 1); - } - - buf = sc->buf; - ptr = sc->ptr; - - /* - * At that point, if ptr == 0, then the message was empty; - * otherwise, there is between 1 and 64 bytes (inclusive) which - * are yet to be processed. Either way, we complete the buffer - * to a full block with zeros (the Skein specification mandates - * that an empty message is padded so that there is at least - * one block to process). - * - * Once this block has been processed, we do it again, with - * a block full of zeros, for the output (that block contains - * the encoding of "0", over 8 bytes, then padded with zeros). - */ - READ_STATE_BIG(sc); - memset(buf + ptr, 0, (sizeof sc->buf) - ptr); - et = 352 + ((bcount == 0) << 7) + (n != 0); - for (i = 0; i < 2; i ++) { - UBI_BIG(et, ptr); - if (i == 0) { - memset(buf, 0, sizeof sc->buf); - bcount = 0; - et = 510; - ptr = 8; - } - } - -#if SPH_SMALL_FOOTPRINT_SKEIN - - /* - * We use a temporary buffer because we must support the case - * where output size is not a multiple of 64 (namely, a 224-bit - * output). - */ - for (u = 0; u < out_len; u += 8) - sph_enc64le_aligned(buf + u, h[u >> 3]); - memcpy(dst, buf, out_len); - -#else - - sph_enc64le_aligned(buf + 0, h0); - sph_enc64le_aligned(buf + 8, h1); - sph_enc64le_aligned(buf + 16, h2); - sph_enc64le_aligned(buf + 24, h3); - sph_enc64le_aligned(buf + 32, h4); - sph_enc64le_aligned(buf + 40, h5); - sph_enc64le_aligned(buf + 48, h6); - sph_enc64le_aligned(buf + 56, h7); - memcpy(dst, buf, out_len); - -#endif -} - -#if 0 -/* obsolete */ -static const sph_u64 IV224[] = { - SPH_C64(0xC6098A8C9AE5EA0B), SPH_C64(0x876D568608C5191C), - SPH_C64(0x99CB88D7D7F53884), SPH_C64(0x384BDDB1AEDDB5DE) -}; - -static const sph_u64 IV256[] = { - SPH_C64(0xFC9DA860D048B449), SPH_C64(0x2FCA66479FA7D833), - SPH_C64(0xB33BC3896656840F), SPH_C64(0x6A54E920FDE8DA69) -}; -#endif - -static const sph_u64 IV224[] = { - SPH_C64(0xCCD0616248677224), SPH_C64(0xCBA65CF3A92339EF), - SPH_C64(0x8CCD69D652FF4B64), SPH_C64(0x398AED7B3AB890B4), - SPH_C64(0x0F59D1B1457D2BD0), SPH_C64(0x6776FE6575D4EB3D), - SPH_C64(0x99FBC70E997413E9), SPH_C64(0x9E2CFCCFE1C41EF7) -}; - -static const sph_u64 IV256[] = { - SPH_C64(0xCCD044A12FDB3E13), SPH_C64(0xE83590301A79A9EB), - SPH_C64(0x55AEA0614F816E6F), SPH_C64(0x2A2767A4AE9B94DB), - SPH_C64(0xEC06025E74DD7683), SPH_C64(0xE7A436CDC4746251), - SPH_C64(0xC36FBAF9393AD185), SPH_C64(0x3EEDBA1833EDFC13) -}; - -static const sph_u64 IV384[] = { - SPH_C64(0xA3F6C6BF3A75EF5F), SPH_C64(0xB0FEF9CCFD84FAA4), - SPH_C64(0x9D77DD663D770CFE), SPH_C64(0xD798CBF3B468FDDA), - SPH_C64(0x1BC4A6668A0E4465), SPH_C64(0x7ED7D434E5807407), - SPH_C64(0x548FC1ACD4EC44D6), SPH_C64(0x266E17546AA18FF8) -}; - -static const sph_u64 IV512[] = { - SPH_C64(0x4903ADFF749C51CE), SPH_C64(0x0D95DE399746DF03), - SPH_C64(0x8FD1934127C79BCE), SPH_C64(0x9A255629FF352CB1), - SPH_C64(0x5DB62599DF6CA7B0), SPH_C64(0xEABE394CA9D5C3F4), - SPH_C64(0x991112C71A75B523), SPH_C64(0xAE18A40B660FCC33) -}; - -#if 0 -/* obsolete */ -/* see sph_skein.h */ -void -sph_skein224_init(void *cc) -{ - skein_small_init(cc, IV224); -} - -/* see sph_skein.h */ -void -sph_skein224(void *cc, const void *data, size_t len) -{ - skein_small_core(cc, data, len); -} - -/* see sph_skein.h */ -void -sph_skein224_close(void *cc, void *dst) -{ - sph_skein224_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_skein.h */ -void -sph_skein224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - skein_small_close(cc, ub, n, dst, 28); - sph_skein224_init(cc); -} - -/* see sph_skein.h */ -void -sph_skein256_init(void *cc) -{ - skein_small_init(cc, IV256); -} - -/* see sph_skein.h */ -void -sph_skein256(void *cc, const void *data, size_t len) -{ - skein_small_core(cc, data, len); -} - -/* see sph_skein.h */ -void -sph_skein256_close(void *cc, void *dst) -{ - sph_skein256_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_skein.h */ -void -sph_skein256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - skein_small_close(cc, ub, n, dst, 32); - sph_skein256_init(cc); -} -#endif - -/* see sph_skein.h */ -void -sph_skein224_init(void *cc) -{ - skein_big_init(cc, IV224); -} - -/* see sph_skein.h */ -void -sph_skein224(void *cc, const void *data, size_t len) -{ - skein_big_core(cc, data, len); -} - -/* see sph_skein.h */ -void -sph_skein224_close(void *cc, void *dst) -{ - sph_skein224_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_skein.h */ -void -sph_skein224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - skein_big_close(cc, ub, n, dst, 28); - sph_skein224_init(cc); -} - -/* see sph_skein.h */ -void -sph_skein256_init(void *cc) -{ - skein_big_init(cc, IV256); -} - -/* see sph_skein.h */ -void -sph_skein256(void *cc, const void *data, size_t len) -{ - skein_big_core(cc, data, len); -} - -/* see sph_skein.h */ -void -sph_skein256_close(void *cc, void *dst) -{ - sph_skein256_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_skein.h */ -void -sph_skein256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - skein_big_close(cc, ub, n, dst, 32); - sph_skein256_init(cc); -} - -/* see sph_skein.h */ -void -sph_skein384_init(void *cc) -{ - skein_big_init(cc, IV384); -} - -/* see sph_skein.h */ -void -sph_skein384(void *cc, const void *data, size_t len) -{ - skein_big_core(cc, data, len); -} - -/* see sph_skein.h */ -void -sph_skein384_close(void *cc, void *dst) -{ - sph_skein384_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_skein.h */ -void -sph_skein384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - skein_big_close(cc, ub, n, dst, 48); - sph_skein384_init(cc); -} - -/* see sph_skein.h */ -void -sph_skein512_init(void *cc) -{ - skein_big_init(cc, IV512); -} - -/* see sph_skein.h */ -void -sph_skein512(void *cc, const void *data, size_t len) -{ - skein_big_core(cc, data, len); -} - -/* see sph_skein.h */ -void -sph_skein512_close(void *cc, void *dst) -{ - sph_skein512_addbits_and_close(cc, 0, 0, dst); -} - -/* see sph_skein.h */ -void -sph_skein512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) -{ - skein_big_close(cc, ub, n, dst, 64); - sph_skein512_init(cc); -} - -#endif - - -#ifdef __cplusplus -} -#endif diff --git a/src/crypto/sph_blake.h b/src/crypto/sph_blake.h deleted file mode 100644 index d8d794399d00..000000000000 --- a/src/crypto/sph_blake.h +++ /dev/null @@ -1,327 +0,0 @@ -/* $Id: sph_blake.h 252 2011-06-07 17:55:14Z tp $ */ -/** - * BLAKE interface. BLAKE is a family of functions which differ by their - * output size; this implementation defines BLAKE for output sizes 224, - * 256, 384 and 512 bits. This implementation conforms to the "third - * round" specification. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @file sph_blake.h - * @author Thomas Pornin - */ - -#ifndef SPH_BLAKE_H__ -#define SPH_BLAKE_H__ - -#ifdef __cplusplus -extern "C"{ -#endif - -#include -#include "sph_types.h" - -/** - * Output size (in bits) for BLAKE-224. - */ -#define SPH_SIZE_blake224 224 - -/** - * Output size (in bits) for BLAKE-256. - */ -#define SPH_SIZE_blake256 256 - -#if SPH_64 - -/** - * Output size (in bits) for BLAKE-384. - */ -#define SPH_SIZE_blake384 384 - -/** - * Output size (in bits) for BLAKE-512. - */ -#define SPH_SIZE_blake512 512 - -#endif - -/** - * This structure is a context for BLAKE-224 and BLAKE-256 computations: - * it contains the intermediate values and some data from the last - * entered block. Once a BLAKE computation has been performed, the - * context can be reused for another computation. - * - * The contents of this structure are private. A running BLAKE - * computation can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[64]; /* first field, for alignment */ - size_t ptr; - sph_u32 H[8]; - sph_u32 S[4]; - sph_u32 T0, T1; -#endif -} sph_blake_small_context; - -/** - * This structure is a context for BLAKE-224 computations. It is - * identical to the common sph_blake_small_context. - */ -typedef sph_blake_small_context sph_blake224_context; - -/** - * This structure is a context for BLAKE-256 computations. It is - * identical to the common sph_blake_small_context. - */ -typedef sph_blake_small_context sph_blake256_context; - -#if SPH_64 - -/** - * This structure is a context for BLAKE-384 and BLAKE-512 computations: - * it contains the intermediate values and some data from the last - * entered block. Once a BLAKE computation has been performed, the - * context can be reused for another computation. - * - * The contents of this structure are private. A running BLAKE - * computation can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[128]; /* first field, for alignment */ - size_t ptr; - sph_u64 H[8]; - sph_u64 S[4]; - sph_u64 T0, T1; -#endif -} sph_blake_big_context; - -/** - * This structure is a context for BLAKE-384 computations. It is - * identical to the common sph_blake_small_context. - */ -typedef sph_blake_big_context sph_blake384_context; - -/** - * This structure is a context for BLAKE-512 computations. It is - * identical to the common sph_blake_small_context. - */ -typedef sph_blake_big_context sph_blake512_context; - -#endif - -/** - * Initialize a BLAKE-224 context. This process performs no memory allocation. - * - * @param cc the BLAKE-224 context (pointer to a - * sph_blake224_context) - */ -void sph_blake224_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the BLAKE-224 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_blake224(void *cc, const void *data, size_t len); - -/** - * Terminate the current BLAKE-224 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (28 bytes). The context is automatically - * reinitialized. - * - * @param cc the BLAKE-224 context - * @param dst the destination buffer - */ -void sph_blake224_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (28 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the BLAKE-224 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_blake224_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a BLAKE-256 context. This process performs no memory allocation. - * - * @param cc the BLAKE-256 context (pointer to a - * sph_blake256_context) - */ -void sph_blake256_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the BLAKE-256 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_blake256(void *cc, const void *data, size_t len); - -/** - * Terminate the current BLAKE-256 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (32 bytes). The context is automatically - * reinitialized. - * - * @param cc the BLAKE-256 context - * @param dst the destination buffer - */ -void sph_blake256_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (32 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the BLAKE-256 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_blake256_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -#if SPH_64 - -/** - * Initialize a BLAKE-384 context. This process performs no memory allocation. - * - * @param cc the BLAKE-384 context (pointer to a - * sph_blake384_context) - */ -void sph_blake384_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the BLAKE-384 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_blake384(void *cc, const void *data, size_t len); - -/** - * Terminate the current BLAKE-384 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (48 bytes). The context is automatically - * reinitialized. - * - * @param cc the BLAKE-384 context - * @param dst the destination buffer - */ -void sph_blake384_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (48 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the BLAKE-384 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_blake384_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a BLAKE-512 context. This process performs no memory allocation. - * - * @param cc the BLAKE-512 context (pointer to a - * sph_blake512_context) - */ -void sph_blake512_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the BLAKE-512 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_blake512(void *cc, const void *data, size_t len); - -/** - * Terminate the current BLAKE-512 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (64 bytes). The context is automatically - * reinitialized. - * - * @param cc the BLAKE-512 context - * @param dst the destination buffer - */ -void sph_blake512_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (64 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the BLAKE-512 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_blake512_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/crypto/sph_bmw.h b/src/crypto/sph_bmw.h deleted file mode 100644 index d386b0c14053..000000000000 --- a/src/crypto/sph_bmw.h +++ /dev/null @@ -1,328 +0,0 @@ -/* $Id: sph_bmw.h 216 2010-06-08 09:46:57Z tp $ */ -/** - * BMW interface. BMW (aka "Blue Midnight Wish") is a family of - * functions which differ by their output size; this implementation - * defines BMW for output sizes 224, 256, 384 and 512 bits. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @file sph_bmw.h - * @author Thomas Pornin - */ - -#ifndef SPH_BMW_H__ -#define SPH_BMW_H__ - -#ifdef __cplusplus -extern "C"{ -#endif - -#include -#include "sph_types.h" - -/** - * Output size (in bits) for BMW-224. - */ -#define SPH_SIZE_bmw224 224 - -/** - * Output size (in bits) for BMW-256. - */ -#define SPH_SIZE_bmw256 256 - -#if SPH_64 - -/** - * Output size (in bits) for BMW-384. - */ -#define SPH_SIZE_bmw384 384 - -/** - * Output size (in bits) for BMW-512. - */ -#define SPH_SIZE_bmw512 512 - -#endif - -/** - * This structure is a context for BMW-224 and BMW-256 computations: - * it contains the intermediate values and some data from the last - * entered block. Once a BMW computation has been performed, the - * context can be reused for another computation. - * - * The contents of this structure are private. A running BMW - * computation can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[64]; /* first field, for alignment */ - size_t ptr; - sph_u32 H[16]; -#if SPH_64 - sph_u64 bit_count; -#else - sph_u32 bit_count_high, bit_count_low; -#endif -#endif -} sph_bmw_small_context; - -/** - * This structure is a context for BMW-224 computations. It is - * identical to the common sph_bmw_small_context. - */ -typedef sph_bmw_small_context sph_bmw224_context; - -/** - * This structure is a context for BMW-256 computations. It is - * identical to the common sph_bmw_small_context. - */ -typedef sph_bmw_small_context sph_bmw256_context; - -#if SPH_64 - -/** - * This structure is a context for BMW-384 and BMW-512 computations: - * it contains the intermediate values and some data from the last - * entered block. Once a BMW computation has been performed, the - * context can be reused for another computation. - * - * The contents of this structure are private. A running BMW - * computation can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[128]; /* first field, for alignment */ - size_t ptr; - sph_u64 H[16]; - sph_u64 bit_count; -#endif -} sph_bmw_big_context; - -/** - * This structure is a context for BMW-384 computations. It is - * identical to the common sph_bmw_small_context. - */ -typedef sph_bmw_big_context sph_bmw384_context; - -/** - * This structure is a context for BMW-512 computations. It is - * identical to the common sph_bmw_small_context. - */ -typedef sph_bmw_big_context sph_bmw512_context; - -#endif - -/** - * Initialize a BMW-224 context. This process performs no memory allocation. - * - * @param cc the BMW-224 context (pointer to a - * sph_bmw224_context) - */ -void sph_bmw224_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the BMW-224 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_bmw224(void *cc, const void *data, size_t len); - -/** - * Terminate the current BMW-224 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (28 bytes). The context is automatically - * reinitialized. - * - * @param cc the BMW-224 context - * @param dst the destination buffer - */ -void sph_bmw224_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (28 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the BMW-224 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_bmw224_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a BMW-256 context. This process performs no memory allocation. - * - * @param cc the BMW-256 context (pointer to a - * sph_bmw256_context) - */ -void sph_bmw256_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the BMW-256 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_bmw256(void *cc, const void *data, size_t len); - -/** - * Terminate the current BMW-256 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (32 bytes). The context is automatically - * reinitialized. - * - * @param cc the BMW-256 context - * @param dst the destination buffer - */ -void sph_bmw256_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (32 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the BMW-256 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_bmw256_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -#if SPH_64 - -/** - * Initialize a BMW-384 context. This process performs no memory allocation. - * - * @param cc the BMW-384 context (pointer to a - * sph_bmw384_context) - */ -void sph_bmw384_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the BMW-384 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_bmw384(void *cc, const void *data, size_t len); - -/** - * Terminate the current BMW-384 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (48 bytes). The context is automatically - * reinitialized. - * - * @param cc the BMW-384 context - * @param dst the destination buffer - */ -void sph_bmw384_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (48 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the BMW-384 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_bmw384_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a BMW-512 context. This process performs no memory allocation. - * - * @param cc the BMW-512 context (pointer to a - * sph_bmw512_context) - */ -void sph_bmw512_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the BMW-512 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_bmw512(void *cc, const void *data, size_t len); - -/** - * Terminate the current BMW-512 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (64 bytes). The context is automatically - * reinitialized. - * - * @param cc the BMW-512 context - * @param dst the destination buffer - */ -void sph_bmw512_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (64 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the BMW-512 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_bmw512_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/crypto/sph_cubehash.h b/src/crypto/sph_cubehash.h deleted file mode 100644 index 487a1946ad40..000000000000 --- a/src/crypto/sph_cubehash.h +++ /dev/null @@ -1,292 +0,0 @@ -/* $Id: sph_cubehash.h 180 2010-05-08 02:29:25Z tp $ */ -/** - * CubeHash interface. CubeHash is a family of functions which differ by - * their output size; this implementation defines CubeHash for output - * sizes 224, 256, 384 and 512 bits, with the "standard parameters" - * (CubeHash16/32 with the CubeHash specification notations). - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @file sph_cubehash.h - * @author Thomas Pornin - */ - -#ifndef SPH_CUBEHASH_H__ -#define SPH_CUBEHASH_H__ - -#ifdef __cplusplus -extern "C"{ -#endif - -#include -#include "sph_types.h" - -/** - * Output size (in bits) for CubeHash-224. - */ -#define SPH_SIZE_cubehash224 224 - -/** - * Output size (in bits) for CubeHash-256. - */ -#define SPH_SIZE_cubehash256 256 - -/** - * Output size (in bits) for CubeHash-384. - */ -#define SPH_SIZE_cubehash384 384 - -/** - * Output size (in bits) for CubeHash-512. - */ -#define SPH_SIZE_cubehash512 512 - -/** - * This structure is a context for CubeHash computations: it contains the - * intermediate values and some data from the last entered block. Once - * a CubeHash computation has been performed, the context can be reused for - * another computation. - * - * The contents of this structure are private. A running CubeHash computation - * can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[32]; /* first field, for alignment */ - size_t ptr; - sph_u32 state[32]; -#endif -} sph_cubehash_context; - -/** - * Type for a CubeHash-224 context (identical to the common context). - */ -typedef sph_cubehash_context sph_cubehash224_context; - -/** - * Type for a CubeHash-256 context (identical to the common context). - */ -typedef sph_cubehash_context sph_cubehash256_context; - -/** - * Type for a CubeHash-384 context (identical to the common context). - */ -typedef sph_cubehash_context sph_cubehash384_context; - -/** - * Type for a CubeHash-512 context (identical to the common context). - */ -typedef sph_cubehash_context sph_cubehash512_context; - -/** - * Initialize a CubeHash-224 context. This process performs no memory - * allocation. - * - * @param cc the CubeHash-224 context (pointer to a - * sph_cubehash224_context) - */ -void sph_cubehash224_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the CubeHash-224 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_cubehash224(void *cc, const void *data, size_t len); - -/** - * Terminate the current CubeHash-224 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (28 bytes). The context is automatically - * reinitialized. - * - * @param cc the CubeHash-224 context - * @param dst the destination buffer - */ -void sph_cubehash224_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (28 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the CubeHash-224 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_cubehash224_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a CubeHash-256 context. This process performs no memory - * allocation. - * - * @param cc the CubeHash-256 context (pointer to a - * sph_cubehash256_context) - */ -void sph_cubehash256_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the CubeHash-256 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_cubehash256(void *cc, const void *data, size_t len); - -/** - * Terminate the current CubeHash-256 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (32 bytes). The context is automatically - * reinitialized. - * - * @param cc the CubeHash-256 context - * @param dst the destination buffer - */ -void sph_cubehash256_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (32 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the CubeHash-256 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_cubehash256_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a CubeHash-384 context. This process performs no memory - * allocation. - * - * @param cc the CubeHash-384 context (pointer to a - * sph_cubehash384_context) - */ -void sph_cubehash384_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the CubeHash-384 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_cubehash384(void *cc, const void *data, size_t len); - -/** - * Terminate the current CubeHash-384 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (48 bytes). The context is automatically - * reinitialized. - * - * @param cc the CubeHash-384 context - * @param dst the destination buffer - */ -void sph_cubehash384_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (48 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the CubeHash-384 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_cubehash384_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a CubeHash-512 context. This process performs no memory - * allocation. - * - * @param cc the CubeHash-512 context (pointer to a - * sph_cubehash512_context) - */ -void sph_cubehash512_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the CubeHash-512 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_cubehash512(void *cc, const void *data, size_t len); - -/** - * Terminate the current CubeHash-512 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (64 bytes). The context is automatically - * reinitialized. - * - * @param cc the CubeHash-512 context - * @param dst the destination buffer - */ -void sph_cubehash512_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (64 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the CubeHash-512 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_cubehash512_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/crypto/sph_groestl.h b/src/crypto/sph_groestl.h deleted file mode 100644 index 495f05e21174..000000000000 --- a/src/crypto/sph_groestl.h +++ /dev/null @@ -1,329 +0,0 @@ -/* $Id: sph_groestl.h 216 2010-06-08 09:46:57Z tp $ */ -/** - * Groestl interface. This code implements Groestl with the recommended - * parameters for SHA-3, with outputs of 224, 256, 384 and 512 bits. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @file sph_groestl.h - * @author Thomas Pornin - */ - -#ifndef SPH_GROESTL_H__ -#define SPH_GROESTL_H__ - -#ifdef __cplusplus -extern "C"{ -#endif - -#include -#include "sph_types.h" - -/** - * Output size (in bits) for Groestl-224. - */ -#define SPH_SIZE_groestl224 224 - -/** - * Output size (in bits) for Groestl-256. - */ -#define SPH_SIZE_groestl256 256 - -/** - * Output size (in bits) for Groestl-384. - */ -#define SPH_SIZE_groestl384 384 - -/** - * Output size (in bits) for Groestl-512. - */ -#define SPH_SIZE_groestl512 512 - -/** - * This structure is a context for Groestl-224 and Groestl-256 computations: - * it contains the intermediate values and some data from the last - * entered block. Once a Groestl computation has been performed, the - * context can be reused for another computation. - * - * The contents of this structure are private. A running Groestl - * computation can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[64]; /* first field, for alignment */ - size_t ptr; - union { -#if SPH_64 - sph_u64 wide[8]; -#endif - sph_u32 narrow[16]; - } state; -#if SPH_64 - sph_u64 count; -#else - sph_u32 count_high, count_low; -#endif -#endif -} sph_groestl_small_context; - -/** - * This structure is a context for Groestl-224 computations. It is - * identical to the common sph_groestl_small_context. - */ -typedef sph_groestl_small_context sph_groestl224_context; - -/** - * This structure is a context for Groestl-256 computations. It is - * identical to the common sph_groestl_small_context. - */ -typedef sph_groestl_small_context sph_groestl256_context; - -/** - * This structure is a context for Groestl-384 and Groestl-512 computations: - * it contains the intermediate values and some data from the last - * entered block. Once a Groestl computation has been performed, the - * context can be reused for another computation. - * - * The contents of this structure are private. A running Groestl - * computation can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[128]; /* first field, for alignment */ - size_t ptr; - union { -#if SPH_64 - sph_u64 wide[16]; -#endif - sph_u32 narrow[32]; - } state; -#if SPH_64 - sph_u64 count; -#else - sph_u32 count_high, count_low; -#endif -#endif -} sph_groestl_big_context; - -/** - * This structure is a context for Groestl-384 computations. It is - * identical to the common sph_groestl_small_context. - */ -typedef sph_groestl_big_context sph_groestl384_context; - -/** - * This structure is a context for Groestl-512 computations. It is - * identical to the common sph_groestl_small_context. - */ -typedef sph_groestl_big_context sph_groestl512_context; - -/** - * Initialize a Groestl-224 context. This process performs no memory allocation. - * - * @param cc the Groestl-224 context (pointer to a - * sph_groestl224_context) - */ -void sph_groestl224_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Groestl-224 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_groestl224(void *cc, const void *data, size_t len); - -/** - * Terminate the current Groestl-224 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (28 bytes). The context is automatically - * reinitialized. - * - * @param cc the Groestl-224 context - * @param dst the destination buffer - */ -void sph_groestl224_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (28 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Groestl-224 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_groestl224_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Groestl-256 context. This process performs no memory allocation. - * - * @param cc the Groestl-256 context (pointer to a - * sph_groestl256_context) - */ -void sph_groestl256_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Groestl-256 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_groestl256(void *cc, const void *data, size_t len); - -/** - * Terminate the current Groestl-256 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (32 bytes). The context is automatically - * reinitialized. - * - * @param cc the Groestl-256 context - * @param dst the destination buffer - */ -void sph_groestl256_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (32 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Groestl-256 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_groestl256_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Groestl-384 context. This process performs no memory allocation. - * - * @param cc the Groestl-384 context (pointer to a - * sph_groestl384_context) - */ -void sph_groestl384_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Groestl-384 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_groestl384(void *cc, const void *data, size_t len); - -/** - * Terminate the current Groestl-384 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (48 bytes). The context is automatically - * reinitialized. - * - * @param cc the Groestl-384 context - * @param dst the destination buffer - */ -void sph_groestl384_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (48 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Groestl-384 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_groestl384_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Groestl-512 context. This process performs no memory allocation. - * - * @param cc the Groestl-512 context (pointer to a - * sph_groestl512_context) - */ -void sph_groestl512_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Groestl-512 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_groestl512(void *cc, const void *data, size_t len); - -/** - * Terminate the current Groestl-512 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (64 bytes). The context is automatically - * reinitialized. - * - * @param cc the Groestl-512 context - * @param dst the destination buffer - */ -void sph_groestl512_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (64 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Groestl-512 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_groestl512_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/crypto/sph_keccak.h b/src/crypto/sph_keccak.h deleted file mode 100644 index bdafdb88db02..000000000000 --- a/src/crypto/sph_keccak.h +++ /dev/null @@ -1,293 +0,0 @@ -/* $Id: sph_keccak.h 216 2010-06-08 09:46:57Z tp $ */ -/** - * Keccak interface. This is the interface for Keccak with the - * recommended parameters for SHA-3, with output lengths 224, 256, - * 384 and 512 bits. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @file sph_keccak.h - * @author Thomas Pornin - */ - -#ifndef SPH_KECCAK_H__ -#define SPH_KECCAK_H__ - -#ifdef __cplusplus -extern "C"{ -#endif - -#include -#include "sph_types.h" - -/** - * Output size (in bits) for Keccak-224. - */ -#define SPH_SIZE_keccak224 224 - -/** - * Output size (in bits) for Keccak-256. - */ -#define SPH_SIZE_keccak256 256 - -/** - * Output size (in bits) for Keccak-384. - */ -#define SPH_SIZE_keccak384 384 - -/** - * Output size (in bits) for Keccak-512. - */ -#define SPH_SIZE_keccak512 512 - -/** - * This structure is a context for Keccak computations: it contains the - * intermediate values and some data from the last entered block. Once a - * Keccak computation has been performed, the context can be reused for - * another computation. - * - * The contents of this structure are private. A running Keccak computation - * can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[144]; /* first field, for alignment */ - size_t ptr, lim; - union { -#if SPH_64 - sph_u64 wide[25]; -#endif - sph_u32 narrow[50]; - } u; -#endif -} sph_keccak_context; - -/** - * Type for a Keccak-224 context (identical to the common context). - */ -typedef sph_keccak_context sph_keccak224_context; - -/** - * Type for a Keccak-256 context (identical to the common context). - */ -typedef sph_keccak_context sph_keccak256_context; - -/** - * Type for a Keccak-384 context (identical to the common context). - */ -typedef sph_keccak_context sph_keccak384_context; - -/** - * Type for a Keccak-512 context (identical to the common context). - */ -typedef sph_keccak_context sph_keccak512_context; - -/** - * Initialize a Keccak-224 context. This process performs no memory allocation. - * - * @param cc the Keccak-224 context (pointer to a - * sph_keccak224_context) - */ -void sph_keccak224_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Keccak-224 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_keccak224(void *cc, const void *data, size_t len); - -/** - * Terminate the current Keccak-224 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (28 bytes). The context is automatically - * reinitialized. - * - * @param cc the Keccak-224 context - * @param dst the destination buffer - */ -void sph_keccak224_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (28 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Keccak-224 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_keccak224_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Keccak-256 context. This process performs no memory allocation. - * - * @param cc the Keccak-256 context (pointer to a - * sph_keccak256_context) - */ -void sph_keccak256_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Keccak-256 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_keccak256(void *cc, const void *data, size_t len); - -/** - * Terminate the current Keccak-256 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (32 bytes). The context is automatically - * reinitialized. - * - * @param cc the Keccak-256 context - * @param dst the destination buffer - */ -void sph_keccak256_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (32 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Keccak-256 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_keccak256_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Keccak-384 context. This process performs no memory allocation. - * - * @param cc the Keccak-384 context (pointer to a - * sph_keccak384_context) - */ -void sph_keccak384_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Keccak-384 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_keccak384(void *cc, const void *data, size_t len); - -/** - * Terminate the current Keccak-384 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (48 bytes). The context is automatically - * reinitialized. - * - * @param cc the Keccak-384 context - * @param dst the destination buffer - */ -void sph_keccak384_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (48 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Keccak-384 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_keccak384_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Keccak-512 context. This process performs no memory allocation. - * - * @param cc the Keccak-512 context (pointer to a - * sph_keccak512_context) - */ -void sph_keccak512_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Keccak-512 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_keccak512(void *cc, const void *data, size_t len); - -/** - * Terminate the current Keccak-512 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (64 bytes). The context is automatically - * reinitialized. - * - * @param cc the Keccak-512 context - * @param dst the destination buffer - */ -void sph_keccak512_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (64 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Keccak-512 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_keccak512_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/crypto/sph_skein.h b/src/crypto/sph_skein.h deleted file mode 100644 index bddbc86fa5aa..000000000000 --- a/src/crypto/sph_skein.h +++ /dev/null @@ -1,298 +0,0 @@ -/* $Id: sph_skein.h 253 2011-06-07 18:33:10Z tp $ */ -/** - * Skein interface. The Skein specification defines three main - * functions, called Skein-256, Skein-512 and Skein-1024, which can be - * further parameterized with an output length. For the SHA-3 - * competition, Skein-512 is used for output sizes of 224, 256, 384 and - * 512 bits; this is what this code implements. Thus, we hereafter call - * Skein-224, Skein-256, Skein-384 and Skein-512 what the Skein - * specification defines as Skein-512-224, Skein-512-256, Skein-512-384 - * and Skein-512-512, respectively. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @file sph_skein.h - * @author Thomas Pornin - */ - -#ifndef SPH_SKEIN_H__ -#define SPH_SKEIN_H__ - -#ifdef __cplusplus -extern "C"{ -#endif - -#include -#include "sph_types.h" - -#if SPH_64 - -/** - * Output size (in bits) for Skein-224. - */ -#define SPH_SIZE_skein224 224 - -/** - * Output size (in bits) for Skein-256. - */ -#define SPH_SIZE_skein256 256 - -/** - * Output size (in bits) for Skein-384. - */ -#define SPH_SIZE_skein384 384 - -/** - * Output size (in bits) for Skein-512. - */ -#define SPH_SIZE_skein512 512 - -/** - * This structure is a context for Skein computations (with a 384- or - * 512-bit output): it contains the intermediate values and some data - * from the last entered block. Once a Skein computation has been - * performed, the context can be reused for another computation. - * - * The contents of this structure are private. A running Skein computation - * can be cloned by copying the context (e.g. with a simple - * memcpy()). - */ -typedef struct { -#ifndef DOXYGEN_IGNORE - unsigned char buf[64]; /* first field, for alignment */ - size_t ptr; - sph_u64 h0, h1, h2, h3, h4, h5, h6, h7; - sph_u64 bcount; -#endif -} sph_skein_big_context; - -/** - * Type for a Skein-224 context (identical to the common "big" context). - */ -typedef sph_skein_big_context sph_skein224_context; - -/** - * Type for a Skein-256 context (identical to the common "big" context). - */ -typedef sph_skein_big_context sph_skein256_context; - -/** - * Type for a Skein-384 context (identical to the common "big" context). - */ -typedef sph_skein_big_context sph_skein384_context; - -/** - * Type for a Skein-512 context (identical to the common "big" context). - */ -typedef sph_skein_big_context sph_skein512_context; - -/** - * Initialize a Skein-224 context. This process performs no memory allocation. - * - * @param cc the Skein-224 context (pointer to a - * sph_skein224_context) - */ -void sph_skein224_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Skein-224 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_skein224(void *cc, const void *data, size_t len); - -/** - * Terminate the current Skein-224 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (28 bytes). The context is automatically - * reinitialized. - * - * @param cc the Skein-224 context - * @param dst the destination buffer - */ -void sph_skein224_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (28 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Skein-224 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_skein224_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Skein-256 context. This process performs no memory allocation. - * - * @param cc the Skein-256 context (pointer to a - * sph_skein256_context) - */ -void sph_skein256_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Skein-256 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_skein256(void *cc, const void *data, size_t len); - -/** - * Terminate the current Skein-256 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (32 bytes). The context is automatically - * reinitialized. - * - * @param cc the Skein-256 context - * @param dst the destination buffer - */ -void sph_skein256_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (32 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Skein-256 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_skein256_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Skein-384 context. This process performs no memory allocation. - * - * @param cc the Skein-384 context (pointer to a - * sph_skein384_context) - */ -void sph_skein384_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Skein-384 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_skein384(void *cc, const void *data, size_t len); - -/** - * Terminate the current Skein-384 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (48 bytes). The context is automatically - * reinitialized. - * - * @param cc the Skein-384 context - * @param dst the destination buffer - */ -void sph_skein384_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (48 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Skein-384 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_skein384_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -/** - * Initialize a Skein-512 context. This process performs no memory allocation. - * - * @param cc the Skein-512 context (pointer to a - * sph_skein512_context) - */ -void sph_skein512_init(void *cc); - -/** - * Process some data bytes. It is acceptable that len is zero - * (in which case this function does nothing). - * - * @param cc the Skein-512 context - * @param data the input data - * @param len the input data length (in bytes) - */ -void sph_skein512(void *cc, const void *data, size_t len); - -/** - * Terminate the current Skein-512 computation and output the result into - * the provided buffer. The destination buffer must be wide enough to - * accomodate the result (64 bytes). The context is automatically - * reinitialized. - * - * @param cc the Skein-512 context - * @param dst the destination buffer - */ -void sph_skein512_close(void *cc, void *dst); - -/** - * Add a few additional bits (0 to 7) to the current computation, then - * terminate it and output the result in the provided buffer, which must - * be wide enough to accomodate the result (64 bytes). If bit number i - * in ub has value 2^i, then the extra bits are those - * numbered 7 downto 8-n (this is the big-endian convention at the byte - * level). The context is automatically reinitialized. - * - * @param cc the Skein-512 context - * @param ub the extra bits - * @param n the number of extra bits (0 to 7) - * @param dst the destination buffer - */ -void sph_skein512_addbits_and_close( - void *cc, unsigned ub, unsigned n, void *dst); - -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/crypto/sph_types.h b/src/crypto/sph_types.h deleted file mode 100644 index 7295b0b37097..000000000000 --- a/src/crypto/sph_types.h +++ /dev/null @@ -1,1976 +0,0 @@ -/* $Id: sph_types.h 260 2011-07-21 01:02:38Z tp $ */ -/** - * Basic type definitions. - * - * This header file defines the generic integer types that will be used - * for the implementation of hash functions; it also contains helper - * functions which encode and decode multi-byte integer values, using - * either little-endian or big-endian conventions. - * - * This file contains a compile-time test on the size of a byte - * (the unsigned char C type). If bytes are not octets, - * i.e. if they do not have a size of exactly 8 bits, then compilation - * is aborted. Architectures where bytes are not octets are relatively - * rare, even in the embedded devices market. We forbid non-octet bytes - * because there is no clear convention on how octet streams are encoded - * on such systems. - * - * ==========================(LICENSE BEGIN)============================ - * - * Copyright (c) 2007-2010 Projet RNRT SAPHIR - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * ===========================(LICENSE END)============================= - * - * @file sph_types.h - * @author Thomas Pornin - */ - -#ifndef SPH_TYPES_H__ -#define SPH_TYPES_H__ - -#include - -/* - * All our I/O functions are defined over octet streams. We do not know - * how to handle input data if bytes are not octets. - */ -#if CHAR_BIT != 8 -#error This code requires 8-bit bytes -#endif - -/* ============= BEGIN documentation block for Doxygen ============ */ - -#ifdef DOXYGEN_IGNORE - -/** @mainpage sphlib C code documentation - * - * @section overview Overview - * - * sphlib is a library which contains implementations of - * various cryptographic hash functions. These pages have been generated - * with doxygen and - * document the API for the C implementations. - * - * The API is described in appropriate header files, which are available - * in the "Files" section. Each hash function family has its own header, - * whose name begins with "sph_" and contains the family - * name. For instance, the API for the RIPEMD hash functions is available - * in the header file sph_ripemd.h. - * - * @section principles API structure and conventions - * - * @subsection io Input/output conventions - * - * In all generality, hash functions operate over strings of bits. - * Individual bits are rarely encountered in C programming or actual - * communication protocols; most protocols converge on the ubiquitous - * "octet" which is a group of eight bits. Data is thus expressed as a - * stream of octets. The C programming language contains the notion of a - * "byte", which is a data unit managed under the type "unsigned - * char". The C standard prescribes that a byte should hold at - * least eight bits, but possibly more. Most modern architectures, even - * in the embedded world, feature eight-bit bytes, i.e. map bytes to - * octets. - * - * Nevertheless, for some of the implemented hash functions, an extra - * API has been added, which allows the input of arbitrary sequences of - * bits: when the computation is about to be closed, 1 to 7 extra bits - * can be added. The functions for which this API is implemented include - * the SHA-2 functions and all SHA-3 candidates. - * - * sphlib defines hash function which may hash octet streams, - * i.e. streams of bits where the number of bits is a multiple of eight. - * The data input functions in the sphlib API expect data - * as anonymous pointers ("const void *") with a length - * (of type "size_t") which gives the input data chunk length - * in bytes. A byte is assumed to be an octet; the sph_types.h - * header contains a compile-time test which prevents compilation on - * architectures where this property is not met. - * - * The hash function output is also converted into bytes. All currently - * implemented hash functions have an output width which is a multiple of - * eight, and this is likely to remain true for new designs. - * - * Most hash functions internally convert input data into 32-bit of 64-bit - * words, using either little-endian or big-endian conversion. The hash - * output also often consists of such words, which are encoded into output - * bytes with a similar endianness convention. Some hash functions have - * been only loosely specified on that subject; when necessary, - * sphlib has been tested against published "reference" - * implementations in order to use the same conventions. - * - * @subsection shortname Function short name - * - * Each implemented hash function has a "short name" which is used - * internally to derive the identifiers for the functions and context - * structures which the function uses. For instance, MD5 has the short - * name "md5". Short names are listed in the next section, - * for the implemented hash functions. In subsequent sections, the - * short name will be assumed to be "XXX": replace with the - * actual hash function name to get the C identifier. - * - * Note: some functions within the same family share the same core - * elements, such as update function or context structure. Correspondingly, - * some of the defined types or functions may actually be macros which - * transparently evaluate to another type or function name. - * - * @subsection context Context structure - * - * Each implemented hash fonction has its own context structure, available - * under the type name "sph_XXX_context" for the hash function - * with short name "XXX". This structure holds all needed - * state for a running hash computation. - * - * The contents of these structures are meant to be opaque, and private - * to the implementation. However, these contents are specified in the - * header files so that application code which uses sphlib - * may access the size of those structures. - * - * The caller is responsible for allocating the context structure, - * whether by dynamic allocation (malloc() or equivalent), - * static allocation (a global permanent variable), as an automatic - * variable ("on the stack"), or by any other mean which ensures proper - * structure alignment. sphlib code performs no dynamic - * allocation by itself. - * - * The context must be initialized before use, using the - * sph_XXX_init() function. This function sets the context - * state to proper initial values for hashing. - * - * Since all state data is contained within the context structure, - * sphlib is thread-safe and reentrant: several hash - * computations may be performed in parallel, provided that they do not - * operate on the same context. Moreover, a running computation can be - * cloned by copying the context (with a simple memcpy()): - * the context and its clone are then independant and may be updated - * with new data and/or closed without interfering with each other. - * Similarly, a context structure can be moved in memory at will: - * context structures contain no pointer, in particular no pointer to - * themselves. - * - * @subsection dataio Data input - * - * Hashed data is input with the sph_XXX() fonction, which - * takes as parameters a pointer to the context, a pointer to the data - * to hash, and the number of data bytes to hash. The context is updated - * with the new data. - * - * Data can be input in one or several calls, with arbitrary input lengths. - * However, it is best, performance wise, to input data by relatively big - * chunks (say a few kilobytes), because this allows sphlib to - * optimize things and avoid internal copying. - * - * When all data has been input, the context can be closed with - * sph_XXX_close(). The hash output is computed and written - * into the provided buffer. The caller must take care to provide a - * buffer of appropriate length; e.g., when using SHA-1, the output is - * a 20-byte word, therefore the output buffer must be at least 20-byte - * long. - * - * For some hash functions, the sph_XXX_addbits_and_close() - * function can be used instead of sph_XXX_close(). This - * function can take a few extra bits to be added at - * the end of the input message. This allows hashing messages with a - * bit length which is not a multiple of 8. The extra bits are provided - * as an unsigned integer value, and a bit count. The bit count must be - * between 0 and 7, inclusive. The extra bits are provided as bits 7 to - * 0 (bits of numerical value 128, 64, 32... downto 0), in that order. - * For instance, to add three bits of value 1, 1 and 0, the unsigned - * integer will have value 192 (1*128 + 1*64 + 0*32) and the bit count - * will be 3. - * - * The SPH_SIZE_XXX macro is defined for each hash function; - * it evaluates to the function output size, expressed in bits. For instance, - * SPH_SIZE_sha1 evaluates to 160. - * - * When closed, the context is automatically reinitialized and can be - * immediately used for another computation. It is not necessary to call - * sph_XXX_init() after a close. Note that - * sph_XXX_init() can still be called to "reset" a context, - * i.e. forget previously input data, and get back to the initial state. - * - * @subsection alignment Data alignment - * - * "Alignment" is a property of data, which is said to be "properly - * aligned" when its emplacement in memory is such that the data can - * be optimally read by full words. This depends on the type of access; - * basically, some hash functions will read data by 32-bit or 64-bit - * words. sphlib does not mandate such alignment for input - * data, but using aligned data can substantially improve performance. - * - * As a rule, it is best to input data by chunks whose length (in bytes) - * is a multiple of eight, and which begins at "generally aligned" - * addresses, such as the base address returned by a call to - * malloc(). - * - * @section functions Implemented functions - * - * We give here the list of implemented functions. They are grouped by - * family; to each family corresponds a specific header file. Each - * individual function has its associated "short name". Please refer to - * the documentation for that header file to get details on the hash - * function denomination and provenance. - * - * Note: the functions marked with a '(64)' in the list below are - * available only if the C compiler provides an integer type of length - * 64 bits or more. Such a type is mandatory in the latest C standard - * (ISO 9899:1999, aka "C99") and is present in several older compilers - * as well, so chances are that such a type is available. - * - * - HAVAL family: file sph_haval.h - * - HAVAL-128/3 (128-bit, 3 passes): short name: haval128_3 - * - HAVAL-128/4 (128-bit, 4 passes): short name: haval128_4 - * - HAVAL-128/5 (128-bit, 5 passes): short name: haval128_5 - * - HAVAL-160/3 (160-bit, 3 passes): short name: haval160_3 - * - HAVAL-160/4 (160-bit, 4 passes): short name: haval160_4 - * - HAVAL-160/5 (160-bit, 5 passes): short name: haval160_5 - * - HAVAL-192/3 (192-bit, 3 passes): short name: haval192_3 - * - HAVAL-192/4 (192-bit, 4 passes): short name: haval192_4 - * - HAVAL-192/5 (192-bit, 5 passes): short name: haval192_5 - * - HAVAL-224/3 (224-bit, 3 passes): short name: haval224_3 - * - HAVAL-224/4 (224-bit, 4 passes): short name: haval224_4 - * - HAVAL-224/5 (224-bit, 5 passes): short name: haval224_5 - * - HAVAL-256/3 (256-bit, 3 passes): short name: haval256_3 - * - HAVAL-256/4 (256-bit, 4 passes): short name: haval256_4 - * - HAVAL-256/5 (256-bit, 5 passes): short name: haval256_5 - * - MD2: file sph_md2.h, short name: md2 - * - MD4: file sph_md4.h, short name: md4 - * - MD5: file sph_md5.h, short name: md5 - * - PANAMA: file sph_panama.h, short name: panama - * - RadioGatun family: file sph_radiogatun.h - * - RadioGatun[32]: short name: radiogatun32 - * - RadioGatun[64]: short name: radiogatun64 (64) - * - RIPEMD family: file sph_ripemd.h - * - RIPEMD: short name: ripemd - * - RIPEMD-128: short name: ripemd128 - * - RIPEMD-160: short name: ripemd160 - * - SHA-0: file sph_sha0.h, short name: sha0 - * - SHA-1: file sph_sha1.h, short name: sha1 - * - SHA-2 family, 32-bit hashes: file sph_sha2.h - * - SHA-224: short name: sha224 - * - SHA-256: short name: sha256 - * - SHA-384: short name: sha384 (64) - * - SHA-512: short name: sha512 (64) - * - Tiger family: file sph_tiger.h - * - Tiger: short name: tiger (64) - * - Tiger2: short name: tiger2 (64) - * - WHIRLPOOL family: file sph_whirlpool.h - * - WHIRLPOOL-0: short name: whirlpool0 (64) - * - WHIRLPOOL-1: short name: whirlpool1 (64) - * - WHIRLPOOL: short name: whirlpool (64) - * - * The fourteen second-round SHA-3 candidates are also implemented; - * when applicable, the implementations follow the "final" specifications - * as published for the third round of the SHA-3 competition (BLAKE, - * Groestl, JH, Keccak and Skein have been tweaked for third round). - * - * - BLAKE family: file sph_blake.h - * - BLAKE-224: short name: blake224 - * - BLAKE-256: short name: blake256 - * - BLAKE-384: short name: blake384 - * - BLAKE-512: short name: blake512 - * - BMW (Blue Midnight Wish) family: file sph_bmw.h - * - BMW-224: short name: bmw224 - * - BMW-256: short name: bmw256 - * - BMW-384: short name: bmw384 (64) - * - BMW-512: short name: bmw512 (64) - * - CubeHash family: file sph_cubehash.h (specified as - * CubeHash16/32 in the CubeHash specification) - * - CubeHash-224: short name: cubehash224 - * - CubeHash-256: short name: cubehash256 - * - CubeHash-384: short name: cubehash384 - * - CubeHash-512: short name: cubehash512 - * - ECHO family: file sph_echo.h - * - ECHO-224: short name: echo224 - * - ECHO-256: short name: echo256 - * - ECHO-384: short name: echo384 - * - ECHO-512: short name: echo512 - * - Fugue family: file sph_fugue.h - * - Fugue-224: short name: fugue224 - * - Fugue-256: short name: fugue256 - * - Fugue-384: short name: fugue384 - * - Fugue-512: short name: fugue512 - * - Groestl family: file sph_groestl.h - * - Groestl-224: short name: groestl224 - * - Groestl-256: short name: groestl256 - * - Groestl-384: short name: groestl384 - * - Groestl-512: short name: groestl512 - * - Hamsi family: file sph_hamsi.h - * - Hamsi-224: short name: hamsi224 - * - Hamsi-256: short name: hamsi256 - * - Hamsi-384: short name: hamsi384 - * - Hamsi-512: short name: hamsi512 - * - JH family: file sph_jh.h - * - JH-224: short name: jh224 - * - JH-256: short name: jh256 - * - JH-384: short name: jh384 - * - JH-512: short name: jh512 - * - Keccak family: file sph_keccak.h - * - Keccak-224: short name: keccak224 - * - Keccak-256: short name: keccak256 - * - Keccak-384: short name: keccak384 - * - Keccak-512: short name: keccak512 - * - Luffa family: file sph_luffa.h - * - Luffa-224: short name: luffa224 - * - Luffa-256: short name: luffa256 - * - Luffa-384: short name: luffa384 - * - Luffa-512: short name: luffa512 - * - Shabal family: file sph_shabal.h - * - Shabal-192: short name: shabal192 - * - Shabal-224: short name: shabal224 - * - Shabal-256: short name: shabal256 - * - Shabal-384: short name: shabal384 - * - Shabal-512: short name: shabal512 - * - SHAvite-3 family: file sph_shavite.h - * - SHAvite-224 (nominally "SHAvite-3 with 224-bit output"): - * short name: shabal224 - * - SHAvite-256 (nominally "SHAvite-3 with 256-bit output"): - * short name: shabal256 - * - SHAvite-384 (nominally "SHAvite-3 with 384-bit output"): - * short name: shabal384 - * - SHAvite-512 (nominally "SHAvite-3 with 512-bit output"): - * short name: shabal512 - * - SIMD family: file sph_simd.h - * - SIMD-224: short name: simd224 - * - SIMD-256: short name: simd256 - * - SIMD-384: short name: simd384 - * - SIMD-512: short name: simd512 - * - Skein family: file sph_skein.h - * - Skein-224 (nominally specified as Skein-512-224): short name: - * skein224 (64) - * - Skein-256 (nominally specified as Skein-512-256): short name: - * skein256 (64) - * - Skein-384 (nominally specified as Skein-512-384): short name: - * skein384 (64) - * - Skein-512 (nominally specified as Skein-512-512): short name: - * skein512 (64) - * - * For the second-round SHA-3 candidates, the functions are as specified - * for round 2, i.e. with the "tweaks" that some candidates added - * between round 1 and round 2. Also, some of the submitted packages for - * round 2 contained errors, in the specification, reference code, or - * both. sphlib implements the corrected versions. - */ - -/** @hideinitializer - * Unsigned integer type whose length is at least 32 bits; on most - * architectures, it will have a width of exactly 32 bits. Unsigned C - * types implement arithmetics modulo a power of 2; use the - * SPH_T32() macro to ensure that the value is truncated - * to exactly 32 bits. Unless otherwise specified, all macros and - * functions which accept sph_u32 values assume that these - * values fit on 32 bits, i.e. do not exceed 2^32-1, even on architectures - * where sph_u32 is larger than that. - */ -typedef __arch_dependant__ sph_u32; - -/** @hideinitializer - * Signed integer type corresponding to sph_u32; it has - * width 32 bits or more. - */ -typedef __arch_dependant__ sph_s32; - -/** @hideinitializer - * Unsigned integer type whose length is at least 64 bits; on most - * architectures which feature such a type, it will have a width of - * exactly 64 bits. C99-compliant platform will have this type; it - * is also defined when the GNU compiler (gcc) is used, and on - * platforms where unsigned long is large enough. If this - * type is not available, then some hash functions which depends on - * a 64-bit type will not be available (most notably SHA-384, SHA-512, - * Tiger and WHIRLPOOL). - */ -typedef __arch_dependant__ sph_u64; - -/** @hideinitializer - * Signed integer type corresponding to sph_u64; it has - * width 64 bits or more. - */ -typedef __arch_dependant__ sph_s64; - -/** - * This macro expands the token x into a suitable - * constant expression of type sph_u32. Depending on - * how this type is defined, a suffix such as UL may - * be appended to the argument. - * - * @param x the token to expand into a suitable constant expression - */ -#define SPH_C32(x) - -/** - * Truncate a 32-bit value to exactly 32 bits. On most systems, this is - * a no-op, recognized as such by the compiler. - * - * @param x the value to truncate (of type sph_u32) - */ -#define SPH_T32(x) - -/** - * Rotate a 32-bit value by a number of bits to the left. The rotate - * count must reside between 1 and 31. This macro assumes that its - * first argument fits in 32 bits (no extra bit allowed on machines where - * sph_u32 is wider); both arguments may be evaluated - * several times. - * - * @param x the value to rotate (of type sph_u32) - * @param n the rotation count (between 1 and 31, inclusive) - */ -#define SPH_ROTL32(x, n) - -/** - * Rotate a 32-bit value by a number of bits to the left. The rotate - * count must reside between 1 and 31. This macro assumes that its - * first argument fits in 32 bits (no extra bit allowed on machines where - * sph_u32 is wider); both arguments may be evaluated - * several times. - * - * @param x the value to rotate (of type sph_u32) - * @param n the rotation count (between 1 and 31, inclusive) - */ -#define SPH_ROTR32(x, n) - -/** - * This macro is defined on systems for which a 64-bit type has been - * detected, and is used for sph_u64. - */ -#define SPH_64 - -/** - * This macro is defined on systems for the "native" integer size is - * 64 bits (64-bit values fit in one register). - */ -#define SPH_64_TRUE - -/** - * This macro expands the token x into a suitable - * constant expression of type sph_u64. Depending on - * how this type is defined, a suffix such as ULL may - * be appended to the argument. This macro is defined only if a - * 64-bit type was detected and used for sph_u64. - * - * @param x the token to expand into a suitable constant expression - */ -#define SPH_C64(x) - -/** - * Truncate a 64-bit value to exactly 64 bits. On most systems, this is - * a no-op, recognized as such by the compiler. This macro is defined only - * if a 64-bit type was detected and used for sph_u64. - * - * @param x the value to truncate (of type sph_u64) - */ -#define SPH_T64(x) - -/** - * Rotate a 64-bit value by a number of bits to the left. The rotate - * count must reside between 1 and 63. This macro assumes that its - * first argument fits in 64 bits (no extra bit allowed on machines where - * sph_u64 is wider); both arguments may be evaluated - * several times. This macro is defined only if a 64-bit type was detected - * and used for sph_u64. - * - * @param x the value to rotate (of type sph_u64) - * @param n the rotation count (between 1 and 63, inclusive) - */ -#define SPH_ROTL64(x, n) - -/** - * Rotate a 64-bit value by a number of bits to the left. The rotate - * count must reside between 1 and 63. This macro assumes that its - * first argument fits in 64 bits (no extra bit allowed on machines where - * sph_u64 is wider); both arguments may be evaluated - * several times. This macro is defined only if a 64-bit type was detected - * and used for sph_u64. - * - * @param x the value to rotate (of type sph_u64) - * @param n the rotation count (between 1 and 63, inclusive) - */ -#define SPH_ROTR64(x, n) - -/** - * This macro evaluates to inline or an equivalent construction, - * if available on the compilation platform, or to nothing otherwise. This - * is used to declare inline functions, for which the compiler should - * endeavour to include the code directly in the caller. Inline functions - * are typically defined in header files as replacement for macros. - */ -#define SPH_INLINE - -/** - * This macro is defined if the platform has been detected as using - * little-endian convention. This implies that the sph_u32 - * type (and the sph_u64 type also, if it is defined) has - * an exact width (i.e. exactly 32-bit, respectively 64-bit). - */ -#define SPH_LITTLE_ENDIAN - -/** - * This macro is defined if the platform has been detected as using - * big-endian convention. This implies that the sph_u32 - * type (and the sph_u64 type also, if it is defined) has - * an exact width (i.e. exactly 32-bit, respectively 64-bit). - */ -#define SPH_BIG_ENDIAN - -/** - * This macro is defined if 32-bit words (and 64-bit words, if defined) - * can be read from and written to memory efficiently in little-endian - * convention. This is the case for little-endian platforms, and also - * for the big-endian platforms which have special little-endian access - * opcodes (e.g. Ultrasparc). - */ -#define SPH_LITTLE_FAST - -/** - * This macro is defined if 32-bit words (and 64-bit words, if defined) - * can be read from and written to memory efficiently in big-endian - * convention. This is the case for little-endian platforms, and also - * for the little-endian platforms which have special big-endian access - * opcodes. - */ -#define SPH_BIG_FAST - -/** - * On some platforms, this macro is defined to an unsigned integer type - * into which pointer values may be cast. The resulting value can then - * be tested for being a multiple of 2, 4 or 8, indicating an aligned - * pointer for, respectively, 16-bit, 32-bit or 64-bit memory accesses. - */ -#define SPH_UPTR - -/** - * When defined, this macro indicates that unaligned memory accesses - * are possible with only a minor penalty, and thus should be prefered - * over strategies which first copy data to an aligned buffer. - */ -#define SPH_UNALIGNED - -/** - * Byte-swap a 32-bit word (i.e. 0x12345678 becomes - * 0x78563412). This is an inline function which resorts - * to inline assembly on some platforms, for better performance. - * - * @param x the 32-bit value to byte-swap - * @return the byte-swapped value - */ -static inline sph_u32 sph_bswap32(sph_u32 x); - -/** - * Byte-swap a 64-bit word. This is an inline function which resorts - * to inline assembly on some platforms, for better performance. This - * function is defined only if a suitable 64-bit type was found for - * sph_u64 - * - * @param x the 64-bit value to byte-swap - * @return the byte-swapped value - */ -static inline sph_u64 sph_bswap64(sph_u64 x); - -/** - * Decode a 16-bit unsigned value from memory, in little-endian convention - * (least significant byte comes first). - * - * @param src the source address - * @return the decoded value - */ -static inline unsigned sph_dec16le(const void *src); - -/** - * Encode a 16-bit unsigned value into memory, in little-endian convention - * (least significant byte comes first). - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc16le(void *dst, unsigned val); - -/** - * Decode a 16-bit unsigned value from memory, in big-endian convention - * (most significant byte comes first). - * - * @param src the source address - * @return the decoded value - */ -static inline unsigned sph_dec16be(const void *src); - -/** - * Encode a 16-bit unsigned value into memory, in big-endian convention - * (most significant byte comes first). - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc16be(void *dst, unsigned val); - -/** - * Decode a 32-bit unsigned value from memory, in little-endian convention - * (least significant byte comes first). - * - * @param src the source address - * @return the decoded value - */ -static inline sph_u32 sph_dec32le(const void *src); - -/** - * Decode a 32-bit unsigned value from memory, in little-endian convention - * (least significant byte comes first). This function assumes that the - * source address is suitably aligned for a direct access, if the platform - * supports such things; it can thus be marginally faster than the generic - * sph_dec32le() function. - * - * @param src the source address - * @return the decoded value - */ -static inline sph_u32 sph_dec32le_aligned(const void *src); - -/** - * Encode a 32-bit unsigned value into memory, in little-endian convention - * (least significant byte comes first). - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc32le(void *dst, sph_u32 val); - -/** - * Encode a 32-bit unsigned value into memory, in little-endian convention - * (least significant byte comes first). This function assumes that the - * destination address is suitably aligned for a direct access, if the - * platform supports such things; it can thus be marginally faster than - * the generic sph_enc32le() function. - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc32le_aligned(void *dst, sph_u32 val); - -/** - * Decode a 32-bit unsigned value from memory, in big-endian convention - * (most significant byte comes first). - * - * @param src the source address - * @return the decoded value - */ -static inline sph_u32 sph_dec32be(const void *src); - -/** - * Decode a 32-bit unsigned value from memory, in big-endian convention - * (most significant byte comes first). This function assumes that the - * source address is suitably aligned for a direct access, if the platform - * supports such things; it can thus be marginally faster than the generic - * sph_dec32be() function. - * - * @param src the source address - * @return the decoded value - */ -static inline sph_u32 sph_dec32be_aligned(const void *src); - -/** - * Encode a 32-bit unsigned value into memory, in big-endian convention - * (most significant byte comes first). - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc32be(void *dst, sph_u32 val); - -/** - * Encode a 32-bit unsigned value into memory, in big-endian convention - * (most significant byte comes first). This function assumes that the - * destination address is suitably aligned for a direct access, if the - * platform supports such things; it can thus be marginally faster than - * the generic sph_enc32be() function. - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc32be_aligned(void *dst, sph_u32 val); - -/** - * Decode a 64-bit unsigned value from memory, in little-endian convention - * (least significant byte comes first). This function is defined only - * if a suitable 64-bit type was detected and used for sph_u64. - * - * @param src the source address - * @return the decoded value - */ -static inline sph_u64 sph_dec64le(const void *src); - -/** - * Decode a 64-bit unsigned value from memory, in little-endian convention - * (least significant byte comes first). This function assumes that the - * source address is suitably aligned for a direct access, if the platform - * supports such things; it can thus be marginally faster than the generic - * sph_dec64le() function. This function is defined only - * if a suitable 64-bit type was detected and used for sph_u64. - * - * @param src the source address - * @return the decoded value - */ -static inline sph_u64 sph_dec64le_aligned(const void *src); - -/** - * Encode a 64-bit unsigned value into memory, in little-endian convention - * (least significant byte comes first). This function is defined only - * if a suitable 64-bit type was detected and used for sph_u64. - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc64le(void *dst, sph_u64 val); - -/** - * Encode a 64-bit unsigned value into memory, in little-endian convention - * (least significant byte comes first). This function assumes that the - * destination address is suitably aligned for a direct access, if the - * platform supports such things; it can thus be marginally faster than - * the generic sph_enc64le() function. This function is defined - * only if a suitable 64-bit type was detected and used for - * sph_u64. - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc64le_aligned(void *dst, sph_u64 val); - -/** - * Decode a 64-bit unsigned value from memory, in big-endian convention - * (most significant byte comes first). This function is defined only - * if a suitable 64-bit type was detected and used for sph_u64. - * - * @param src the source address - * @return the decoded value - */ -static inline sph_u64 sph_dec64be(const void *src); - -/** - * Decode a 64-bit unsigned value from memory, in big-endian convention - * (most significant byte comes first). This function assumes that the - * source address is suitably aligned for a direct access, if the platform - * supports such things; it can thus be marginally faster than the generic - * sph_dec64be() function. This function is defined only - * if a suitable 64-bit type was detected and used for sph_u64. - * - * @param src the source address - * @return the decoded value - */ -static inline sph_u64 sph_dec64be_aligned(const void *src); - -/** - * Encode a 64-bit unsigned value into memory, in big-endian convention - * (most significant byte comes first). This function is defined only - * if a suitable 64-bit type was detected and used for sph_u64. - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc64be(void *dst, sph_u64 val); - -/** - * Encode a 64-bit unsigned value into memory, in big-endian convention - * (most significant byte comes first). This function assumes that the - * destination address is suitably aligned for a direct access, if the - * platform supports such things; it can thus be marginally faster than - * the generic sph_enc64be() function. This function is defined - * only if a suitable 64-bit type was detected and used for - * sph_u64. - * - * @param dst the destination buffer - * @param val the value to encode - */ -static inline void sph_enc64be_aligned(void *dst, sph_u64 val); - -#endif - -/* ============== END documentation block for Doxygen ============= */ - -#ifndef DOXYGEN_IGNORE - -/* - * We want to define the types "sph_u32" and "sph_u64" which hold - * unsigned values of at least, respectively, 32 and 64 bits. These - * tests should select appropriate types for most platforms. The - * macro "SPH_64" is defined if the 64-bit is supported. - */ - -#undef SPH_64 -#undef SPH_64_TRUE - -#if defined __STDC__ && __STDC_VERSION__ >= 199901L - -/* - * On C99 implementations, we can use to get an exact 64-bit - * type, if any, or otherwise use a wider type (which must exist, for - * C99 conformance). - */ - -#include - -#ifdef UINT32_MAX -typedef uint32_t sph_u32; -typedef int32_t sph_s32; -#else -typedef uint_fast32_t sph_u32; -typedef int_fast32_t sph_s32; -#endif -#if !SPH_NO_64 -#ifdef UINT64_MAX -typedef uint64_t sph_u64; -typedef int64_t sph_s64; -#else -typedef uint_fast64_t sph_u64; -typedef int_fast64_t sph_s64; -#endif -#endif - -#define SPH_C32(x) ((sph_u32)(x)) -#if !SPH_NO_64 -#define SPH_C64(x) ((sph_u64)(x)) -#define SPH_64 1 -#endif - -#else - -/* - * On non-C99 systems, we use "unsigned int" if it is wide enough, - * "unsigned long" otherwise. This supports all "reasonable" architectures. - * We have to be cautious: pre-C99 preprocessors handle constants - * differently in '#if' expressions. Hence the shifts to test UINT_MAX. - */ - -#if ((UINT_MAX >> 11) >> 11) >= 0x3FF - -typedef unsigned int sph_u32; -typedef int sph_s32; - -#define SPH_C32(x) ((sph_u32)(x ## U)) - -#else - -typedef unsigned long sph_u32; -typedef long sph_s32; - -#define SPH_C32(x) ((sph_u32)(x ## UL)) - -#endif - -#if !SPH_NO_64 - -/* - * We want a 64-bit type. We use "unsigned long" if it is wide enough (as - * is common on 64-bit architectures such as AMD64, Alpha or Sparcv9), - * "unsigned long long" otherwise, if available. We use ULLONG_MAX to - * test whether "unsigned long long" is available; we also know that - * gcc features this type, even if the libc header do not know it. - */ - -#if ((ULONG_MAX >> 31) >> 31) >= 3 - -typedef unsigned long sph_u64; -typedef long sph_s64; - -#define SPH_C64(x) ((sph_u64)(x ## UL)) - -#define SPH_64 1 - -#elif ((ULLONG_MAX >> 31) >> 31) >= 3 || defined __GNUC__ - -typedef unsigned long long sph_u64; -typedef long long sph_s64; - -#define SPH_C64(x) ((sph_u64)(x ## ULL)) - -#define SPH_64 1 - -#else - -/* - * No 64-bit type... - */ - -#endif - -#endif - -#endif - -/* - * If the "unsigned long" type has length 64 bits or more, then this is - * a "true" 64-bit architectures. This is also true with Visual C on - * amd64, even though the "long" type is limited to 32 bits. - */ -#if SPH_64 && (((ULONG_MAX >> 31) >> 31) >= 3 || defined _M_X64) -#define SPH_64_TRUE 1 -#endif - -/* - * Implementation note: some processors have specific opcodes to perform - * a rotation. Recent versions of gcc recognize the expression above and - * use the relevant opcodes, when appropriate. - */ - -#define SPH_T32(x) ((x) & SPH_C32(0xFFFFFFFF)) -#define SPH_ROTL32(x, n) SPH_T32(((x) << (n)) | ((x) >> (32 - (n)))) -#define SPH_ROTR32(x, n) SPH_ROTL32(x, (32 - (n))) - -#if SPH_64 - -#define SPH_T64(x) ((x) & SPH_C64(0xFFFFFFFFFFFFFFFF)) -#define SPH_ROTL64(x, n) SPH_T64(((x) << (n)) | ((x) >> (64 - (n)))) -#define SPH_ROTR64(x, n) SPH_ROTL64(x, (64 - (n))) - -#endif - -#ifndef DOXYGEN_IGNORE -/* - * Define SPH_INLINE to be an "inline" qualifier, if available. We define - * some small macro-like functions which benefit greatly from being inlined. - */ -#if (defined __STDC__ && __STDC_VERSION__ >= 199901L) || defined __GNUC__ -#define SPH_INLINE inline -#elif defined _MSC_VER -#define SPH_INLINE __inline -#else -#define SPH_INLINE -#endif -#endif - -/* - * We define some macros which qualify the architecture. These macros - * may be explicit set externally (e.g. as compiler parameters). The - * code below sets those macros if they are not already defined. - * - * Most macros are boolean, thus evaluate to either zero or non-zero. - * The SPH_UPTR macro is special, in that it evaluates to a C type, - * or is not defined. - * - * SPH_UPTR if defined: unsigned type to cast pointers into - * - * SPH_UNALIGNED non-zero if unaligned accesses are efficient - * SPH_LITTLE_ENDIAN non-zero if architecture is known to be little-endian - * SPH_BIG_ENDIAN non-zero if architecture is known to be big-endian - * SPH_LITTLE_FAST non-zero if little-endian decoding is fast - * SPH_BIG_FAST non-zero if big-endian decoding is fast - * - * If SPH_UPTR is defined, then encoding and decoding of 32-bit and 64-bit - * values will try to be "smart". Either SPH_LITTLE_ENDIAN or SPH_BIG_ENDIAN - * _must_ be non-zero in those situations. The 32-bit and 64-bit types - * _must_ also have an exact width. - * - * SPH_SPARCV9_GCC_32 UltraSPARC-compatible with gcc, 32-bit mode - * SPH_SPARCV9_GCC_64 UltraSPARC-compatible with gcc, 64-bit mode - * SPH_SPARCV9_GCC UltraSPARC-compatible with gcc - * SPH_I386_GCC x86-compatible (32-bit) with gcc - * SPH_I386_MSVC x86-compatible (32-bit) with Microsoft Visual C - * SPH_AMD64_GCC x86-compatible (64-bit) with gcc - * SPH_AMD64_MSVC x86-compatible (64-bit) with Microsoft Visual C - * SPH_PPC32_GCC PowerPC, 32-bit, with gcc - * SPH_PPC64_GCC PowerPC, 64-bit, with gcc - * - * TODO: enhance automatic detection, for more architectures and compilers. - * Endianness is the most important. SPH_UNALIGNED and SPH_UPTR help with - * some very fast functions (e.g. MD4) when using unaligned input data. - * The CPU-specific-with-GCC macros are useful only for inline assembly, - * normally restrained to this header file. - */ - -/* - * 32-bit x86, aka "i386 compatible". - */ -#if defined __i386__ || defined _M_IX86 - -#define SPH_DETECT_UNALIGNED 1 -#define SPH_DETECT_LITTLE_ENDIAN 1 -#define SPH_DETECT_UPTR sph_u32 -#ifdef __GNUC__ -#define SPH_DETECT_I386_GCC 1 -#endif -#ifdef _MSC_VER -#define SPH_DETECT_I386_MSVC 1 -#endif - -/* - * 64-bit x86, hereafter known as "amd64". - */ -#elif defined __x86_64 || defined _M_X64 - -#define SPH_DETECT_UNALIGNED 1 -#define SPH_DETECT_LITTLE_ENDIAN 1 -#define SPH_DETECT_UPTR sph_u64 -#ifdef __GNUC__ -#define SPH_DETECT_AMD64_GCC 1 -#endif -#ifdef _MSC_VER -#define SPH_DETECT_AMD64_MSVC 1 -#endif - -/* - * 64-bit Sparc architecture (implies v9). - */ -#elif ((defined __sparc__ || defined __sparc) && defined __arch64__) \ - || defined __sparcv9 - -#define SPH_DETECT_BIG_ENDIAN 1 -#define SPH_DETECT_UPTR sph_u64 -#ifdef __GNUC__ -#define SPH_DETECT_SPARCV9_GCC_64 1 -#define SPH_DETECT_LITTLE_FAST 1 -#endif - -/* - * 32-bit Sparc. - */ -#elif (defined __sparc__ || defined __sparc) \ - && !(defined __sparcv9 || defined __arch64__) - -#define SPH_DETECT_BIG_ENDIAN 1 -#define SPH_DETECT_UPTR sph_u32 -#if defined __GNUC__ && defined __sparc_v9__ -#define SPH_DETECT_SPARCV9_GCC_32 1 -#define SPH_DETECT_LITTLE_FAST 1 -#endif - -/* - * ARM, little-endian. - */ -#elif defined __arm__ && __ARMEL__ - -#define SPH_DETECT_LITTLE_ENDIAN 1 - -/* - * MIPS, little-endian. - */ -#elif MIPSEL || _MIPSEL || __MIPSEL || __MIPSEL__ - -#define SPH_DETECT_LITTLE_ENDIAN 1 - -/* - * MIPS, big-endian. - */ -#elif MIPSEB || _MIPSEB || __MIPSEB || __MIPSEB__ - -#define SPH_DETECT_BIG_ENDIAN 1 - -/* - * PowerPC. - */ -#elif defined __powerpc__ || defined __POWERPC__ || defined __ppc__ \ - || defined _ARCH_PPC - -/* - * Note: we do not declare cross-endian access to be "fast": even if - * using inline assembly, implementation should still assume that - * keeping the decoded word in a temporary is faster than decoding - * it again. - */ -#if defined __GNUC__ -#if SPH_64_TRUE -#define SPH_DETECT_PPC64_GCC 1 -#else -#define SPH_DETECT_PPC32_GCC 1 -#endif -#endif - -#if defined __BIG_ENDIAN__ || defined _BIG_ENDIAN -#define SPH_DETECT_BIG_ENDIAN 1 -#elif defined __LITTLE_ENDIAN__ || defined _LITTLE_ENDIAN -#define SPH_DETECT_LITTLE_ENDIAN 1 -#endif - -/* - * Itanium, 64-bit. - */ -#elif defined __ia64 || defined __ia64__ \ - || defined __itanium__ || defined _M_IA64 - -#if defined __BIG_ENDIAN__ || defined _BIG_ENDIAN -#define SPH_DETECT_BIG_ENDIAN 1 -#else -#define SPH_DETECT_LITTLE_ENDIAN 1 -#endif -#if defined __LP64__ || defined _LP64 -#define SPH_DETECT_UPTR sph_u64 -#else -#define SPH_DETECT_UPTR sph_u32 -#endif - -#endif - -#if defined SPH_DETECT_SPARCV9_GCC_32 || defined SPH_DETECT_SPARCV9_GCC_64 -#define SPH_DETECT_SPARCV9_GCC 1 -#endif - -#if defined SPH_DETECT_UNALIGNED && !defined SPH_UNALIGNED -#define SPH_UNALIGNED SPH_DETECT_UNALIGNED -#endif -#if defined SPH_DETECT_UPTR && !defined SPH_UPTR -#define SPH_UPTR SPH_DETECT_UPTR -#endif -#if defined SPH_DETECT_LITTLE_ENDIAN && !defined SPH_LITTLE_ENDIAN -#define SPH_LITTLE_ENDIAN SPH_DETECT_LITTLE_ENDIAN -#endif -#if defined SPH_DETECT_BIG_ENDIAN && !defined SPH_BIG_ENDIAN -#define SPH_BIG_ENDIAN SPH_DETECT_BIG_ENDIAN -#endif -#if defined SPH_DETECT_LITTLE_FAST && !defined SPH_LITTLE_FAST -#define SPH_LITTLE_FAST SPH_DETECT_LITTLE_FAST -#endif -#if defined SPH_DETECT_BIG_FAST && !defined SPH_BIG_FAST -#define SPH_BIG_FAST SPH_DETECT_BIG_FAST -#endif -#if defined SPH_DETECT_SPARCV9_GCC_32 && !defined SPH_SPARCV9_GCC_32 -#define SPH_SPARCV9_GCC_32 SPH_DETECT_SPARCV9_GCC_32 -#endif -#if defined SPH_DETECT_SPARCV9_GCC_64 && !defined SPH_SPARCV9_GCC_64 -#define SPH_SPARCV9_GCC_64 SPH_DETECT_SPARCV9_GCC_64 -#endif -#if defined SPH_DETECT_SPARCV9_GCC && !defined SPH_SPARCV9_GCC -#define SPH_SPARCV9_GCC SPH_DETECT_SPARCV9_GCC -#endif -#if defined SPH_DETECT_I386_GCC && !defined SPH_I386_GCC -#define SPH_I386_GCC SPH_DETECT_I386_GCC -#endif -#if defined SPH_DETECT_I386_MSVC && !defined SPH_I386_MSVC -#define SPH_I386_MSVC SPH_DETECT_I386_MSVC -#endif -#if defined SPH_DETECT_AMD64_GCC && !defined SPH_AMD64_GCC -#define SPH_AMD64_GCC SPH_DETECT_AMD64_GCC -#endif -#if defined SPH_DETECT_AMD64_MSVC && !defined SPH_AMD64_MSVC -#define SPH_AMD64_MSVC SPH_DETECT_AMD64_MSVC -#endif -#if defined SPH_DETECT_PPC32_GCC && !defined SPH_PPC32_GCC -#define SPH_PPC32_GCC SPH_DETECT_PPC32_GCC -#endif -#if defined SPH_DETECT_PPC64_GCC && !defined SPH_PPC64_GCC -#define SPH_PPC64_GCC SPH_DETECT_PPC64_GCC -#endif - -#if SPH_LITTLE_ENDIAN && !defined SPH_LITTLE_FAST -#define SPH_LITTLE_FAST 1 -#endif -#if SPH_BIG_ENDIAN && !defined SPH_BIG_FAST -#define SPH_BIG_FAST 1 -#endif - -#if defined SPH_UPTR && !(SPH_LITTLE_ENDIAN || SPH_BIG_ENDIAN) -#error SPH_UPTR defined, but endianness is not known. -#endif - -#if SPH_I386_GCC && !SPH_NO_ASM - -/* - * On x86 32-bit, with gcc, we use the bswapl opcode to byte-swap 32-bit - * values. - */ - -static SPH_INLINE sph_u32 -sph_bswap32(sph_u32 x) -{ - __asm__ __volatile__ ("bswapl %0" : "=r" (x) : "0" (x)); - return x; -} - -#if SPH_64 - -static SPH_INLINE sph_u64 -sph_bswap64(sph_u64 x) -{ - return ((sph_u64)sph_bswap32((sph_u32)x) << 32) - | (sph_u64)sph_bswap32((sph_u32)(x >> 32)); -} - -#endif - -#elif SPH_AMD64_GCC && !SPH_NO_ASM - -/* - * On x86 64-bit, with gcc, we use the bswapl opcode to byte-swap 32-bit - * and 64-bit values. - */ - -static SPH_INLINE sph_u32 -sph_bswap32(sph_u32 x) -{ - __asm__ __volatile__ ("bswapl %0" : "=r" (x) : "0" (x)); - return x; -} - -#if SPH_64 - -static SPH_INLINE sph_u64 -sph_bswap64(sph_u64 x) -{ - __asm__ __volatile__ ("bswapq %0" : "=r" (x) : "0" (x)); - return x; -} - -#endif - -/* - * Disabled code. Apparently, Microsoft Visual C 2005 is smart enough - * to generate proper opcodes for endianness swapping with the pure C - * implementation below. - * - -#elif SPH_I386_MSVC && !SPH_NO_ASM - -static __inline sph_u32 __declspec(naked) __fastcall -sph_bswap32(sph_u32 x) -{ - __asm { - bswap ecx - mov eax,ecx - ret - } -} - -#if SPH_64 - -static SPH_INLINE sph_u64 -sph_bswap64(sph_u64 x) -{ - return ((sph_u64)sph_bswap32((sph_u32)x) << 32) - | (sph_u64)sph_bswap32((sph_u32)(x >> 32)); -} - -#endif - - * - * [end of disabled code] - */ - -#else - -static SPH_INLINE sph_u32 -sph_bswap32(sph_u32 x) -{ - x = SPH_T32((x << 16) | (x >> 16)); - x = ((x & SPH_C32(0xFF00FF00)) >> 8) - | ((x & SPH_C32(0x00FF00FF)) << 8); - return x; -} - -#if SPH_64 - -/** - * Byte-swap a 64-bit value. - * - * @param x the input value - * @return the byte-swapped value - */ -static SPH_INLINE sph_u64 -sph_bswap64(sph_u64 x) -{ - x = SPH_T64((x << 32) | (x >> 32)); - x = ((x & SPH_C64(0xFFFF0000FFFF0000)) >> 16) - | ((x & SPH_C64(0x0000FFFF0000FFFF)) << 16); - x = ((x & SPH_C64(0xFF00FF00FF00FF00)) >> 8) - | ((x & SPH_C64(0x00FF00FF00FF00FF)) << 8); - return x; -} - -#endif - -#endif - -#if SPH_SPARCV9_GCC && !SPH_NO_ASM - -/* - * On UltraSPARC systems, native ordering is big-endian, but it is - * possible to perform little-endian read accesses by specifying the - * address space 0x88 (ASI_PRIMARY_LITTLE). Basically, either we use - * the opcode "lda [%reg]0x88,%dst", where %reg is the register which - * contains the source address and %dst is the destination register, - * or we use "lda [%reg+imm]%asi,%dst", which uses the %asi register - * to get the address space name. The latter format is better since it - * combines an addition and the actual access in a single opcode; but - * it requires the setting (and subsequent resetting) of %asi, which is - * slow. Some operations (i.e. MD5 compression function) combine many - * successive little-endian read accesses, which may share the same - * %asi setting. The macros below contain the appropriate inline - * assembly. - */ - -#define SPH_SPARCV9_SET_ASI \ - sph_u32 sph_sparcv9_asi; \ - __asm__ __volatile__ ( \ - "rd %%asi,%0\n\twr %%g0,0x88,%%asi" : "=r" (sph_sparcv9_asi)); - -#define SPH_SPARCV9_RESET_ASI \ - __asm__ __volatile__ ("wr %%g0,%0,%%asi" : : "r" (sph_sparcv9_asi)); - -#define SPH_SPARCV9_DEC32LE(base, idx) ({ \ - sph_u32 sph_sparcv9_tmp; \ - __asm__ __volatile__ ("lda [%1+" #idx "*4]%%asi,%0" \ - : "=r" (sph_sparcv9_tmp) : "r" (base)); \ - sph_sparcv9_tmp; \ - }) - -#endif - -static SPH_INLINE void -sph_enc16be(void *dst, unsigned val) -{ - ((unsigned char *)dst)[0] = (val >> 8); - ((unsigned char *)dst)[1] = val; -} - -static SPH_INLINE unsigned -sph_dec16be(const void *src) -{ - return ((unsigned)(((const unsigned char *)src)[0]) << 8) - | (unsigned)(((const unsigned char *)src)[1]); -} - -static SPH_INLINE void -sph_enc16le(void *dst, unsigned val) -{ - ((unsigned char *)dst)[0] = val; - ((unsigned char *)dst)[1] = val >> 8; -} - -static SPH_INLINE unsigned -sph_dec16le(const void *src) -{ - return (unsigned)(((const unsigned char *)src)[0]) - | ((unsigned)(((const unsigned char *)src)[1]) << 8); -} - -/** - * Encode a 32-bit value into the provided buffer (big endian convention). - * - * @param dst the destination buffer - * @param val the 32-bit value to encode - */ -static SPH_INLINE void -sph_enc32be(void *dst, sph_u32 val) -{ -#if defined SPH_UPTR -#if SPH_UNALIGNED -#if SPH_LITTLE_ENDIAN - val = sph_bswap32(val); -#endif - *(sph_u32 *)dst = val; -#else - if (((SPH_UPTR)dst & 3) == 0) { -#if SPH_LITTLE_ENDIAN - val = sph_bswap32(val); -#endif - *(sph_u32 *)dst = val; - } else { - ((unsigned char *)dst)[0] = (val >> 24); - ((unsigned char *)dst)[1] = (val >> 16); - ((unsigned char *)dst)[2] = (val >> 8); - ((unsigned char *)dst)[3] = val; - } -#endif -#else - ((unsigned char *)dst)[0] = (val >> 24); - ((unsigned char *)dst)[1] = (val >> 16); - ((unsigned char *)dst)[2] = (val >> 8); - ((unsigned char *)dst)[3] = val; -#endif -} - -/** - * Encode a 32-bit value into the provided buffer (big endian convention). - * The destination buffer must be properly aligned. - * - * @param dst the destination buffer (32-bit aligned) - * @param val the value to encode - */ -static SPH_INLINE void -sph_enc32be_aligned(void *dst, sph_u32 val) -{ -#if SPH_LITTLE_ENDIAN - *(sph_u32 *)dst = sph_bswap32(val); -#elif SPH_BIG_ENDIAN - *(sph_u32 *)dst = val; -#else - ((unsigned char *)dst)[0] = (val >> 24); - ((unsigned char *)dst)[1] = (val >> 16); - ((unsigned char *)dst)[2] = (val >> 8); - ((unsigned char *)dst)[3] = val; -#endif -} - -/** - * Decode a 32-bit value from the provided buffer (big endian convention). - * - * @param src the source buffer - * @return the decoded value - */ -static SPH_INLINE sph_u32 -sph_dec32be(const void *src) -{ -#if defined SPH_UPTR -#if SPH_UNALIGNED -#if SPH_LITTLE_ENDIAN - return sph_bswap32(*(const sph_u32 *)src); -#else - return *(const sph_u32 *)src; -#endif -#else - if (((SPH_UPTR)src & 3) == 0) { -#if SPH_LITTLE_ENDIAN - return sph_bswap32(*(const sph_u32 *)src); -#else - return *(const sph_u32 *)src; -#endif - } else { - return ((sph_u32)(((const unsigned char *)src)[0]) << 24) - | ((sph_u32)(((const unsigned char *)src)[1]) << 16) - | ((sph_u32)(((const unsigned char *)src)[2]) << 8) - | (sph_u32)(((const unsigned char *)src)[3]); - } -#endif -#else - return ((sph_u32)(((const unsigned char *)src)[0]) << 24) - | ((sph_u32)(((const unsigned char *)src)[1]) << 16) - | ((sph_u32)(((const unsigned char *)src)[2]) << 8) - | (sph_u32)(((const unsigned char *)src)[3]); -#endif -} - -/** - * Decode a 32-bit value from the provided buffer (big endian convention). - * The source buffer must be properly aligned. - * - * @param src the source buffer (32-bit aligned) - * @return the decoded value - */ -static SPH_INLINE sph_u32 -sph_dec32be_aligned(const void *src) -{ -#if SPH_LITTLE_ENDIAN - return sph_bswap32(*(const sph_u32 *)src); -#elif SPH_BIG_ENDIAN - return *(const sph_u32 *)src; -#else - return ((sph_u32)(((const unsigned char *)src)[0]) << 24) - | ((sph_u32)(((const unsigned char *)src)[1]) << 16) - | ((sph_u32)(((const unsigned char *)src)[2]) << 8) - | (sph_u32)(((const unsigned char *)src)[3]); -#endif -} - -/** - * Encode a 32-bit value into the provided buffer (little endian convention). - * - * @param dst the destination buffer - * @param val the 32-bit value to encode - */ -static SPH_INLINE void -sph_enc32le(void *dst, sph_u32 val) -{ -#if defined SPH_UPTR -#if SPH_UNALIGNED -#if SPH_BIG_ENDIAN - val = sph_bswap32(val); -#endif - *(sph_u32 *)dst = val; -#else - if (((SPH_UPTR)dst & 3) == 0) { -#if SPH_BIG_ENDIAN - val = sph_bswap32(val); -#endif - *(sph_u32 *)dst = val; - } else { - ((unsigned char *)dst)[0] = val; - ((unsigned char *)dst)[1] = (val >> 8); - ((unsigned char *)dst)[2] = (val >> 16); - ((unsigned char *)dst)[3] = (val >> 24); - } -#endif -#else - ((unsigned char *)dst)[0] = val; - ((unsigned char *)dst)[1] = (val >> 8); - ((unsigned char *)dst)[2] = (val >> 16); - ((unsigned char *)dst)[3] = (val >> 24); -#endif -} - -/** - * Encode a 32-bit value into the provided buffer (little endian convention). - * The destination buffer must be properly aligned. - * - * @param dst the destination buffer (32-bit aligned) - * @param val the value to encode - */ -static SPH_INLINE void -sph_enc32le_aligned(void *dst, sph_u32 val) -{ -#if SPH_LITTLE_ENDIAN - *(sph_u32 *)dst = val; -#elif SPH_BIG_ENDIAN - *(sph_u32 *)dst = sph_bswap32(val); -#else - ((unsigned char *)dst)[0] = val; - ((unsigned char *)dst)[1] = (val >> 8); - ((unsigned char *)dst)[2] = (val >> 16); - ((unsigned char *)dst)[3] = (val >> 24); -#endif -} - -/** - * Decode a 32-bit value from the provided buffer (little endian convention). - * - * @param src the source buffer - * @return the decoded value - */ -static SPH_INLINE sph_u32 -sph_dec32le(const void *src) -{ -#if defined SPH_UPTR -#if SPH_UNALIGNED -#if SPH_BIG_ENDIAN - return sph_bswap32(*(const sph_u32 *)src); -#else - return *(const sph_u32 *)src; -#endif -#else - if (((SPH_UPTR)src & 3) == 0) { -#if SPH_BIG_ENDIAN -#if SPH_SPARCV9_GCC && !SPH_NO_ASM - sph_u32 tmp; - - /* - * "__volatile__" is needed here because without it, - * gcc-3.4.3 miscompiles the code and performs the - * access before the test on the address, thus triggering - * a bus error... - */ - __asm__ __volatile__ ( - "lda [%1]0x88,%0" : "=r" (tmp) : "r" (src)); - return tmp; -/* - * On PowerPC, this turns out not to be worth the effort: the inline - * assembly makes GCC optimizer uncomfortable, which tends to nullify - * the decoding gains. - * - * For most hash functions, using this inline assembly trick changes - * hashing speed by less than 5% and often _reduces_ it. The biggest - * gains are for MD4 (+11%) and CubeHash (+30%). For all others, it is - * less then 10%. The speed gain on CubeHash is probably due to the - * chronic shortage of registers that CubeHash endures; for the other - * functions, the generic code appears to be efficient enough already. - * -#elif (SPH_PPC32_GCC || SPH_PPC64_GCC) && !SPH_NO_ASM - sph_u32 tmp; - - __asm__ __volatile__ ( - "lwbrx %0,0,%1" : "=r" (tmp) : "r" (src)); - return tmp; - */ -#else - return sph_bswap32(*(const sph_u32 *)src); -#endif -#else - return *(const sph_u32 *)src; -#endif - } else { - return (sph_u32)(((const unsigned char *)src)[0]) - | ((sph_u32)(((const unsigned char *)src)[1]) << 8) - | ((sph_u32)(((const unsigned char *)src)[2]) << 16) - | ((sph_u32)(((const unsigned char *)src)[3]) << 24); - } -#endif -#else - return (sph_u32)(((const unsigned char *)src)[0]) - | ((sph_u32)(((const unsigned char *)src)[1]) << 8) - | ((sph_u32)(((const unsigned char *)src)[2]) << 16) - | ((sph_u32)(((const unsigned char *)src)[3]) << 24); -#endif -} - -/** - * Decode a 32-bit value from the provided buffer (little endian convention). - * The source buffer must be properly aligned. - * - * @param src the source buffer (32-bit aligned) - * @return the decoded value - */ -static SPH_INLINE sph_u32 -sph_dec32le_aligned(const void *src) -{ -#if SPH_LITTLE_ENDIAN - return *(const sph_u32 *)src; -#elif SPH_BIG_ENDIAN -#if SPH_SPARCV9_GCC && !SPH_NO_ASM - sph_u32 tmp; - - __asm__ __volatile__ ("lda [%1]0x88,%0" : "=r" (tmp) : "r" (src)); - return tmp; -/* - * Not worth it generally. - * -#elif (SPH_PPC32_GCC || SPH_PPC64_GCC) && !SPH_NO_ASM - sph_u32 tmp; - - __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (tmp) : "r" (src)); - return tmp; - */ -#else - return sph_bswap32(*(const sph_u32 *)src); -#endif -#else - return (sph_u32)(((const unsigned char *)src)[0]) - | ((sph_u32)(((const unsigned char *)src)[1]) << 8) - | ((sph_u32)(((const unsigned char *)src)[2]) << 16) - | ((sph_u32)(((const unsigned char *)src)[3]) << 24); -#endif -} - -#if SPH_64 - -/** - * Encode a 64-bit value into the provided buffer (big endian convention). - * - * @param dst the destination buffer - * @param val the 64-bit value to encode - */ -static SPH_INLINE void -sph_enc64be(void *dst, sph_u64 val) -{ -#if defined SPH_UPTR -#if SPH_UNALIGNED -#if SPH_LITTLE_ENDIAN - val = sph_bswap64(val); -#endif - *(sph_u64 *)dst = val; -#else - if (((SPH_UPTR)dst & 7) == 0) { -#if SPH_LITTLE_ENDIAN - val = sph_bswap64(val); -#endif - *(sph_u64 *)dst = val; - } else { - ((unsigned char *)dst)[0] = (val >> 56); - ((unsigned char *)dst)[1] = (val >> 48); - ((unsigned char *)dst)[2] = (val >> 40); - ((unsigned char *)dst)[3] = (val >> 32); - ((unsigned char *)dst)[4] = (val >> 24); - ((unsigned char *)dst)[5] = (val >> 16); - ((unsigned char *)dst)[6] = (val >> 8); - ((unsigned char *)dst)[7] = val; - } -#endif -#else - ((unsigned char *)dst)[0] = (val >> 56); - ((unsigned char *)dst)[1] = (val >> 48); - ((unsigned char *)dst)[2] = (val >> 40); - ((unsigned char *)dst)[3] = (val >> 32); - ((unsigned char *)dst)[4] = (val >> 24); - ((unsigned char *)dst)[5] = (val >> 16); - ((unsigned char *)dst)[6] = (val >> 8); - ((unsigned char *)dst)[7] = val; -#endif -} - -/** - * Encode a 64-bit value into the provided buffer (big endian convention). - * The destination buffer must be properly aligned. - * - * @param dst the destination buffer (64-bit aligned) - * @param val the value to encode - */ -static SPH_INLINE void -sph_enc64be_aligned(void *dst, sph_u64 val) -{ -#if SPH_LITTLE_ENDIAN - *(sph_u64 *)dst = sph_bswap64(val); -#elif SPH_BIG_ENDIAN - *(sph_u64 *)dst = val; -#else - ((unsigned char *)dst)[0] = (val >> 56); - ((unsigned char *)dst)[1] = (val >> 48); - ((unsigned char *)dst)[2] = (val >> 40); - ((unsigned char *)dst)[3] = (val >> 32); - ((unsigned char *)dst)[4] = (val >> 24); - ((unsigned char *)dst)[5] = (val >> 16); - ((unsigned char *)dst)[6] = (val >> 8); - ((unsigned char *)dst)[7] = val; -#endif -} - -/** - * Decode a 64-bit value from the provided buffer (big endian convention). - * - * @param src the source buffer - * @return the decoded value - */ -static SPH_INLINE sph_u64 -sph_dec64be(const void *src) -{ -#if defined SPH_UPTR -#if SPH_UNALIGNED -#if SPH_LITTLE_ENDIAN - return sph_bswap64(*(const sph_u64 *)src); -#else - return *(const sph_u64 *)src; -#endif -#else - if (((SPH_UPTR)src & 7) == 0) { -#if SPH_LITTLE_ENDIAN - return sph_bswap64(*(const sph_u64 *)src); -#else - return *(const sph_u64 *)src; -#endif - } else { - return ((sph_u64)(((const unsigned char *)src)[0]) << 56) - | ((sph_u64)(((const unsigned char *)src)[1]) << 48) - | ((sph_u64)(((const unsigned char *)src)[2]) << 40) - | ((sph_u64)(((const unsigned char *)src)[3]) << 32) - | ((sph_u64)(((const unsigned char *)src)[4]) << 24) - | ((sph_u64)(((const unsigned char *)src)[5]) << 16) - | ((sph_u64)(((const unsigned char *)src)[6]) << 8) - | (sph_u64)(((const unsigned char *)src)[7]); - } -#endif -#else - return ((sph_u64)(((const unsigned char *)src)[0]) << 56) - | ((sph_u64)(((const unsigned char *)src)[1]) << 48) - | ((sph_u64)(((const unsigned char *)src)[2]) << 40) - | ((sph_u64)(((const unsigned char *)src)[3]) << 32) - | ((sph_u64)(((const unsigned char *)src)[4]) << 24) - | ((sph_u64)(((const unsigned char *)src)[5]) << 16) - | ((sph_u64)(((const unsigned char *)src)[6]) << 8) - | (sph_u64)(((const unsigned char *)src)[7]); -#endif -} - -/** - * Decode a 64-bit value from the provided buffer (big endian convention). - * The source buffer must be properly aligned. - * - * @param src the source buffer (64-bit aligned) - * @return the decoded value - */ -static SPH_INLINE sph_u64 -sph_dec64be_aligned(const void *src) -{ -#if SPH_LITTLE_ENDIAN - return sph_bswap64(*(const sph_u64 *)src); -#elif SPH_BIG_ENDIAN - return *(const sph_u64 *)src; -#else - return ((sph_u64)(((const unsigned char *)src)[0]) << 56) - | ((sph_u64)(((const unsigned char *)src)[1]) << 48) - | ((sph_u64)(((const unsigned char *)src)[2]) << 40) - | ((sph_u64)(((const unsigned char *)src)[3]) << 32) - | ((sph_u64)(((const unsigned char *)src)[4]) << 24) - | ((sph_u64)(((const unsigned char *)src)[5]) << 16) - | ((sph_u64)(((const unsigned char *)src)[6]) << 8) - | (sph_u64)(((const unsigned char *)src)[7]); -#endif -} - -/** - * Encode a 64-bit value into the provided buffer (little endian convention). - * - * @param dst the destination buffer - * @param val the 64-bit value to encode - */ -static SPH_INLINE void -sph_enc64le(void *dst, sph_u64 val) -{ -#if defined SPH_UPTR -#if SPH_UNALIGNED -#if SPH_BIG_ENDIAN - val = sph_bswap64(val); -#endif - *(sph_u64 *)dst = val; -#else - if (((SPH_UPTR)dst & 7) == 0) { -#if SPH_BIG_ENDIAN - val = sph_bswap64(val); -#endif - *(sph_u64 *)dst = val; - } else { - ((unsigned char *)dst)[0] = val; - ((unsigned char *)dst)[1] = (val >> 8); - ((unsigned char *)dst)[2] = (val >> 16); - ((unsigned char *)dst)[3] = (val >> 24); - ((unsigned char *)dst)[4] = (val >> 32); - ((unsigned char *)dst)[5] = (val >> 40); - ((unsigned char *)dst)[6] = (val >> 48); - ((unsigned char *)dst)[7] = (val >> 56); - } -#endif -#else - ((unsigned char *)dst)[0] = val; - ((unsigned char *)dst)[1] = (val >> 8); - ((unsigned char *)dst)[2] = (val >> 16); - ((unsigned char *)dst)[3] = (val >> 24); - ((unsigned char *)dst)[4] = (val >> 32); - ((unsigned char *)dst)[5] = (val >> 40); - ((unsigned char *)dst)[6] = (val >> 48); - ((unsigned char *)dst)[7] = (val >> 56); -#endif -} - -/** - * Encode a 64-bit value into the provided buffer (little endian convention). - * The destination buffer must be properly aligned. - * - * @param dst the destination buffer (64-bit aligned) - * @param val the value to encode - */ -static SPH_INLINE void -sph_enc64le_aligned(void *dst, sph_u64 val) -{ -#if SPH_LITTLE_ENDIAN - *(sph_u64 *)dst = val; -#elif SPH_BIG_ENDIAN - *(sph_u64 *)dst = sph_bswap64(val); -#else - ((unsigned char *)dst)[0] = val; - ((unsigned char *)dst)[1] = (val >> 8); - ((unsigned char *)dst)[2] = (val >> 16); - ((unsigned char *)dst)[3] = (val >> 24); - ((unsigned char *)dst)[4] = (val >> 32); - ((unsigned char *)dst)[5] = (val >> 40); - ((unsigned char *)dst)[6] = (val >> 48); - ((unsigned char *)dst)[7] = (val >> 56); -#endif -} - -/** - * Decode a 64-bit value from the provided buffer (little endian convention). - * - * @param src the source buffer - * @return the decoded value - */ -static SPH_INLINE sph_u64 -sph_dec64le(const void *src) -{ -#if defined SPH_UPTR -#if SPH_UNALIGNED -#if SPH_BIG_ENDIAN - return sph_bswap64(*(const sph_u64 *)src); -#else - return *(const sph_u64 *)src; -#endif -#else - if (((SPH_UPTR)src & 7) == 0) { -#if SPH_BIG_ENDIAN -#if SPH_SPARCV9_GCC_64 && !SPH_NO_ASM - sph_u64 tmp; - - __asm__ __volatile__ ( - "ldxa [%1]0x88,%0" : "=r" (tmp) : "r" (src)); - return tmp; -/* - * Not worth it generally. - * -#elif SPH_PPC32_GCC && !SPH_NO_ASM - return (sph_u64)sph_dec32le_aligned(src) - | ((sph_u64)sph_dec32le_aligned( - (const char *)src + 4) << 32); -#elif SPH_PPC64_GCC && !SPH_NO_ASM - sph_u64 tmp; - - __asm__ __volatile__ ( - "ldbrx %0,0,%1" : "=r" (tmp) : "r" (src)); - return tmp; - */ -#else - return sph_bswap64(*(const sph_u64 *)src); -#endif -#else - return *(const sph_u64 *)src; -#endif - } else { - return (sph_u64)(((const unsigned char *)src)[0]) - | ((sph_u64)(((const unsigned char *)src)[1]) << 8) - | ((sph_u64)(((const unsigned char *)src)[2]) << 16) - | ((sph_u64)(((const unsigned char *)src)[3]) << 24) - | ((sph_u64)(((const unsigned char *)src)[4]) << 32) - | ((sph_u64)(((const unsigned char *)src)[5]) << 40) - | ((sph_u64)(((const unsigned char *)src)[6]) << 48) - | ((sph_u64)(((const unsigned char *)src)[7]) << 56); - } -#endif -#else - return (sph_u64)(((const unsigned char *)src)[0]) - | ((sph_u64)(((const unsigned char *)src)[1]) << 8) - | ((sph_u64)(((const unsigned char *)src)[2]) << 16) - | ((sph_u64)(((const unsigned char *)src)[3]) << 24) - | ((sph_u64)(((const unsigned char *)src)[4]) << 32) - | ((sph_u64)(((const unsigned char *)src)[5]) << 40) - | ((sph_u64)(((const unsigned char *)src)[6]) << 48) - | ((sph_u64)(((const unsigned char *)src)[7]) << 56); -#endif -} - -/** - * Decode a 64-bit value from the provided buffer (little endian convention). - * The source buffer must be properly aligned. - * - * @param src the source buffer (64-bit aligned) - * @return the decoded value - */ -static SPH_INLINE sph_u64 -sph_dec64le_aligned(const void *src) -{ -#if SPH_LITTLE_ENDIAN - return *(const sph_u64 *)src; -#elif SPH_BIG_ENDIAN -#if SPH_SPARCV9_GCC_64 && !SPH_NO_ASM - sph_u64 tmp; - - __asm__ __volatile__ ("ldxa [%1]0x88,%0" : "=r" (tmp) : "r" (src)); - return tmp; -/* - * Not worth it generally. - * -#elif SPH_PPC32_GCC && !SPH_NO_ASM - return (sph_u64)sph_dec32le_aligned(src) - | ((sph_u64)sph_dec32le_aligned((const char *)src + 4) << 32); -#elif SPH_PPC64_GCC && !SPH_NO_ASM - sph_u64 tmp; - - __asm__ __volatile__ ("ldbrx %0,0,%1" : "=r" (tmp) : "r" (src)); - return tmp; - */ -#else - return sph_bswap64(*(const sph_u64 *)src); -#endif -#else - return (sph_u64)(((const unsigned char *)src)[0]) - | ((sph_u64)(((const unsigned char *)src)[1]) << 8) - | ((sph_u64)(((const unsigned char *)src)[2]) << 16) - | ((sph_u64)(((const unsigned char *)src)[3]) << 24) - | ((sph_u64)(((const unsigned char *)src)[4]) << 32) - | ((sph_u64)(((const unsigned char *)src)[5]) << 40) - | ((sph_u64)(((const unsigned char *)src)[6]) << 48) - | ((sph_u64)(((const unsigned char *)src)[7]) << 56); -#endif -} - -#endif - -#endif /* Doxygen excluded block */ - -#endif diff --git a/src/ctpl.h b/src/ctpl.h new file mode 100644 index 000000000000..64f650d3e83b --- /dev/null +++ b/src/ctpl.h @@ -0,0 +1,240 @@ + +/********************************************************* + * + * Copyright (C) 2014 by Vitaliy Vitsentiy + * + * 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. + * + *********************************************************/ + + +#ifndef __ctpl_thread_pool_H__ +#define __ctpl_thread_pool_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifndef _ctplThreadPoolLength_ +#define _ctplThreadPoolLength_ 100 +#endif + + +// thread pool to run user's functors with signature +// ret func(int id, other_params) +// where id is the index of the thread that runs the functor +// ret is some return type + + +namespace ctpl { + + class thread_pool { + + public: + + thread_pool() : q(_ctplThreadPoolLength_) { this->init(); } + thread_pool(int nThreads, int queueSize = _ctplThreadPoolLength_) : q(queueSize) { this->init(); this->resize(nThreads); } + + // the destructor waits for all the functions in the queue to be finished + ~thread_pool() { + this->stop(true); + } + + // get the number of running threads in the pool + int size() { return static_cast(this->threads.size()); } + + // number of idle threads + int n_idle() { return this->nWaiting; } + std::thread & get_thread(int i) { return *this->threads[i]; } + + // change the number of threads in the pool + // should be called from one thread, otherwise be careful to not interleave, also with this->stop() + // nThreads must be >= 0 + void resize(int nThreads) { + if (!this->isStop && !this->isDone) { + int oldNThreads = static_cast(this->threads.size()); + if (oldNThreads <= nThreads) { // if the number of threads is increased + this->threads.resize(nThreads); + this->flags.resize(nThreads); + + for (int i = oldNThreads; i < nThreads; ++i) { + this->flags[i] = std::make_shared>(false); + this->set_thread(i); + } + } + else { // the number of threads is decreased + for (int i = oldNThreads - 1; i >= nThreads; --i) { + *this->flags[i] = true; // this thread will finish + this->threads[i]->detach(); + } + { + // stop the detached threads that were waiting + std::unique_lock lock(this->mutex); + this->cv.notify_all(); + } + this->threads.resize(nThreads); // safe to delete because the threads are detached + this->flags.resize(nThreads); // safe to delete because the threads have copies of shared_ptr of the flags, not originals + } + } + } + + // empty the queue + void clear_queue() { + std::function * _f; + while (this->q.pop(_f)) + delete _f; // empty the queue + } + + // pops a functional wraper to the original function + std::function pop() { + std::function * _f = nullptr; + this->q.pop(_f); + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + + std::function f; + if (_f) + f = *_f; + return f; + } + + + // wait for all computing threads to finish and stop all threads + // may be called asyncronously to not pause the calling thread while waiting + // if isWait == true, all the functions in the queue are run, otherwise the queue is cleared without running the functions + void stop(bool isWait = false) { + if (!isWait) { + if (this->isStop) + return; + this->isStop = true; + for (int i = 0, n = this->size(); i < n; ++i) { + *this->flags[i] = true; // command the threads to stop + } + this->clear_queue(); // empty the queue + } + else { + if (this->isDone || this->isStop) + return; + this->isDone = true; // give the waiting threads a command to finish + } + { + std::unique_lock lock(this->mutex); + this->cv.notify_all(); // stop all waiting threads + } + for (int i = 0; i < static_cast(this->threads.size()); ++i) { // wait for the computing threads to finish + if (this->threads[i]->joinable()) + this->threads[i]->join(); + } + // if there were no threads in the pool but some functors in the queue, the functors are not deleted by the threads + // therefore delete them here + this->clear_queue(); + this->threads.clear(); + this->flags.clear(); + } + + template + auto push(F && f, Rest&&... rest) ->std::future { + auto pck = std::make_shared>( + std::bind(std::forward(f), std::placeholders::_1, std::forward(rest)...) + ); + + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + + return pck->get_future(); + } + + // run the user's function that excepts argument int - id of the running thread. returned value is templatized + // operator returns std::future, where the user can get the result and rethrow the catched exceptins + template + auto push(F && f) ->std::future { + auto pck = std::make_shared>(std::forward(f)); + + auto _f = new std::function([pck](int id) { + (*pck)(id); + }); + this->q.push(_f); + + std::unique_lock lock(this->mutex); + this->cv.notify_one(); + + return pck->get_future(); + } + + + private: + + // deleted + thread_pool(const thread_pool &);// = delete; + thread_pool(thread_pool &&);// = delete; + thread_pool & operator=(const thread_pool &);// = delete; + thread_pool & operator=(thread_pool &&);// = delete; + + void set_thread(int i) { + std::shared_ptr> flag(this->flags[i]); // a copy of the shared ptr to the flag + auto f = [this, i, flag/* a copy of the shared ptr to the flag */]() { + std::atomic & _flag = *flag; + std::function * _f; + bool isPop = this->q.pop(_f); + while (true) { + while (isPop) { // if there is anything in the queue + std::unique_ptr> func(_f); // at return, delete the function even if an exception occurred + (*_f)(i); + + if (_flag) + return; // the thread is wanted to stop, return even if the queue is not empty yet + else + isPop = this->q.pop(_f); + } + + // the queue is empty here, wait for the next command + std::unique_lock lock(this->mutex); + ++this->nWaiting; + this->cv.wait(lock, [this, &_f, &isPop, &_flag](){ isPop = this->q.pop(_f); return isPop || this->isDone || _flag; }); + --this->nWaiting; + + if (!isPop) + return; // if the queue is empty and this->isDone == true or *flag then return + } + }; + this->threads[i].reset(new std::thread(f)); // compiler may not support std::make_unique() + } + + void init() { this->nWaiting = 0; this->isStop = false; this->isDone = false; } + + std::vector> threads; + std::vector>> flags; + mutable boost::lockfree::queue *> q; + std::atomic isDone; + std::atomic isStop; + std::atomic nWaiting; // how many threads are waiting + + std::mutex mutex; + std::condition_variable cv; + }; + +} + +#endif // __ctpl_thread_pool_H__ + diff --git a/src/dbwrapper.h b/src/dbwrapper.h index 1d590ec73135..500da8380632 100644 --- a/src/dbwrapper.h +++ b/src/dbwrapper.h @@ -12,6 +12,8 @@ #include "utilstrencodings.h" #include "version.h" +#include + #include #include @@ -342,4 +344,218 @@ class CDBWrapper }; +class CDBTransaction { +private: + CDBWrapper &db; + + struct KeyHolder { + virtual ~KeyHolder() = default; + virtual bool Less(const KeyHolder &b) const = 0; + virtual void Erase(CDBBatch &batch) = 0; + }; + typedef std::unique_ptr KeyHolderPtr; + + template + struct KeyHolderImpl : KeyHolder { + KeyHolderImpl(const K &_key) + : key(_key) { + } + virtual bool Less(const KeyHolder &b) const { + auto *b2 = dynamic_cast*>(&b); + return key < b2->key; + } + virtual void Erase(CDBBatch &batch) { + batch.Erase(key); + } + K key; + }; + + struct KeyValueHolder { + virtual ~KeyValueHolder() = default; + virtual void Write(CDBBatch &batch) = 0; + }; + typedef std::unique_ptr KeyValueHolderPtr; + + template + struct KeyValueHolderImpl : KeyValueHolder { + KeyValueHolderImpl(const KeyHolderImpl &_key, const V &_value) + : key(_key), + value(_value) { } + virtual void Write(CDBBatch &batch) { + batch.Write(key.key, value); + } + const KeyHolderImpl &key; + V value; + }; + + struct keyCmp { + bool operator()(const KeyHolderPtr &a, const KeyHolderPtr &b) const { + return a->Less(*b); + } + }; + + typedef std::map KeyValueMap; + typedef std::map TypeKeyValueMap; + + TypeKeyValueMap writes; + TypeKeyValueMap deletes; + + template + KeyValueMap *getMapForType(TypeKeyValueMap &m, bool create) { + auto it = m.find(typeid(K)); + if (it != m.end()) { + return &it->second; + } + if (!create) + return nullptr; + auto it2 = m.emplace(typeid(K), KeyValueMap()); + return &it2.first->second; + } + + template + KeyValueMap *getWritesMap(bool create) { + return getMapForType(writes, create); + } + + template + KeyValueMap *getDeletesMap(bool create) { + return getMapForType(deletes, create); + } + +public: + CDBTransaction(CDBWrapper &_db) : db(_db) {} + + template + void Write(const K& key, const V& value) { + KeyHolderPtr k(new KeyHolderImpl(key)); + KeyHolderImpl* k2 = dynamic_cast*>(k.get()); + KeyValueHolderPtr kv(new KeyValueHolderImpl(*k2, value)); + + KeyValueMap *ds = getDeletesMap(false); + if (ds) + ds->erase(k); + + KeyValueMap *ws = getWritesMap(true); + ws->erase(k); + ws->emplace(std::make_pair(std::move(k), std::move(kv))); + } + + template + bool Read(const K& key, V& value) { + KeyHolderPtr k(new KeyHolderImpl(key)); + + KeyValueMap *ds = getDeletesMap(false); + if (ds && ds->count(k)) + return false; + + KeyValueMap *ws = getWritesMap(false); + if (ws) { + KeyValueMap::iterator it = ws->find(k); + if (it != ws->end()) { + auto *impl = dynamic_cast *>(it->second.get()); + if (!impl) + return false; + value = impl->value; + return true; + } + } + + return db.Read(key, value); + } + + template + bool Exists(const K& key) { + KeyHolderPtr k(new KeyHolderImpl(key)); + + KeyValueMap *ds = getDeletesMap(false); + if (ds && ds->count(k)) + return false; + + KeyValueMap *ws = getWritesMap(false); + if (ws && ws->count(k)) + return true; + + return db.Exists(key); + } + + template + void Erase(const K& key) { + KeyHolderPtr k(new KeyHolderImpl(key)); + + KeyValueMap *ws = getWritesMap(false); + if (ws) + ws->erase(k); + KeyValueMap *ds = getDeletesMap(true); + ds->emplace(std::move(k), nullptr); + } + + void Clear() { + writes.clear(); + deletes.clear(); + } + + bool Commit() { + CDBBatch batch(db); + for (auto &p : deletes) { + for (auto &p2 : p.second) { + p2.first->Erase(batch); + } + } + for (auto &p : writes) { + for (auto &p2 : p.second) { + p2.second->Write(batch); + } + } + bool ret = db.WriteBatch(batch, true); + Clear(); + return ret; + } + + bool IsClean() { + return writes.empty() && deletes.empty(); + } +}; + +class CScopedDBTransaction { +private: + CDBTransaction &dbTransaction; + std::function commitHandler; + std::function rollbackHandler; + bool didCommitOrRollback{}; + +public: + CScopedDBTransaction(CDBTransaction &dbTx) : dbTransaction(dbTx) {} + ~CScopedDBTransaction() { + if (!didCommitOrRollback) + Rollback(); + } + bool Commit() { + assert(!didCommitOrRollback); + didCommitOrRollback = true; + bool result = dbTransaction.Commit(); + if (commitHandler) + commitHandler(); + return result; + } + void Rollback() { + assert(!didCommitOrRollback); + didCommitOrRollback = true; + dbTransaction.Clear(); + if (rollbackHandler) + rollbackHandler(); + } + + static std::unique_ptr Begin(CDBTransaction &dbTx) { + assert(dbTx.IsClean()); + return std::unique_ptr(new CScopedDBTransaction(dbTx)); + } + + void SetCommitHandler(const std::function &h) { + commitHandler = h; + } + void SetRollbackHandler(const std::function &h) { + rollbackHandler = h; + } +}; + #endif // BITCOIN_DBWRAPPER_H diff --git a/src/dsnotificationinterface.cpp b/src/dsnotificationinterface.cpp index 90d572aa0b15..fc74e0556b14 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,6 +15,8 @@ #include "privatesend-client.h" #endif // ENABLE_WALLET +#include "evo/deterministicmns.h" + void CDSNotificationInterface::InitializeCurrentBlockTip() { LOCK(cs_main); @@ -36,10 +38,16 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con if (pindexNew == pindexFork) // blocks were disconnected without any new ones return; + deterministicMNManager->UpdatedBlockTip(pindexNew); + masternodeSync.UpdatedBlockTip(pindexNew, fInitialDownload, connman); // Update global DIP0001 activation status fDIP0001ActiveAtTip = pindexNew->nHeight >= Params().GetConsensus().DIP0001Height; + fAIP0003ActiveAtTip = (VersionBitsState(pindexNew->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_AIP0003, versionbitscache) == THRESHOLD_ACTIVE); + // update instantsend autolock activation flag + instantsend.isAutoLockBip9Active = + (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_ISAUTOLOCKS) == THRESHOLD_ACTIVE); if (fInitialDownload) return; diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp new file mode 100644 index 000000000000..85a56aae7439 --- /dev/null +++ b/src/evo/cbtx.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "cbtx.h" +#include "specialtx.h" +#include "deterministicmns.h" +#include "simplifiedmns.h" + +#include "validation.h" +#include "univalue.h" + +bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + AssertLockHeld(cs_main); + + if (!tx.IsCoinBase()) + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-invalid"); + + CCbTx cbTx; + if (!GetTxPayload(tx, cbTx)) + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + + if (cbTx.nVersion > CCbTx::CURRENT_VERSION) + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-version"); + + if (pindexPrev) { + if (pindexPrev->nHeight + 1 != cbTx.nHeight) + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-height"); + } + + return true; +} + +// This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list +bool CheckCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindex, CValidationState& state) +{ + AssertLockHeld(cs_main); + + if (block.vtx[0]->nType != TRANSACTION_COINBASE) + return true; + + CCbTx cbTx; + if (!GetTxPayload(*block.vtx[0], cbTx)) + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + + if (pindex) { + uint256 calculatedMerkleRoot; + if (!CalcCbTxMerkleRootMNList(block, pindex->pprev, calculatedMerkleRoot, state)) { + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-mnmerkleroot"); + } + if (calculatedMerkleRoot != cbTx.merkleRootMNList) { + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-mnmerkleroot"); + } + } + + return true; +} + +bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state) +{ + AssertLockHeld(cs_main); + LOCK(deterministicMNManager->cs); + + CDeterministicMNList tmpMNList; + if (!deterministicMNManager->BuildNewListFromBlock(block, pindexPrev, state, tmpMNList)) { + return false; + } + + CSimplifiedMNList sml(tmpMNList); + bool mutated = false; + merkleRootRet = sml.CalcMerkleRoot(&mutated); + return !mutated; +} + +std::string CCbTx::ToString() const +{ + return strprintf("CCbTx(nHeight=%d, nVersion=%d, merkleRootMNList=%s)", + nVersion, nHeight, merkleRootMNList.ToString()); +} + +void CCbTx::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("version", (int)nVersion)); + obj.push_back(Pair("height", (int)nHeight)); + obj.push_back(Pair("merkleRootMNList", merkleRootMNList.ToString())); +} diff --git a/src/evo/cbtx.h b/src/evo/cbtx.h new file mode 100644 index 000000000000..177f4ba24992 --- /dev/null +++ b/src/evo/cbtx.h @@ -0,0 +1,46 @@ +// Copyright (c) 2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ABSOLUTE_CBTX_H +#define ABSOLUTE_CBTX_H + +#include "primitives/transaction.h" +#include "consensus/validation.h" + +class CBlock; +class CBlockIndex; +class UniValue; + +// coinbase transaction +class CCbTx +{ +public: + static const uint16_t CURRENT_VERSION = 1; + +public: + uint16_t nVersion{CURRENT_VERSION}; + int32_t nHeight{0}; + uint256 merkleRootMNList; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(nHeight); + READWRITE(merkleRootMNList); + } + + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + +bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); + +bool CheckCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindex, CValidationState& state); +bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state); + +#endif//ABSOLUTE_CBTX_H diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp new file mode 100644 index 000000000000..c3056eea0e5a --- /dev/null +++ b/src/evo/deterministicmns.cpp @@ -0,0 +1,602 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "deterministicmns.h" +#include "specialtx.h" + +#include "validation.h" +#include "validationinterface.h" +#include "chainparams.h" +#include "script/standard.h" +#include "base58.h" +#include "core_io.h" +#include "spork.h" + +#include + +static const std::string DB_LIST_SNAPSHOT = "dmn_S"; +static const std::string DB_LIST_DIFF = "dmn_D"; + +CDeterministicMNManager* deterministicMNManager; + +std::string CDeterministicMNState::ToString() const +{ + CTxDestination dest; + std::string payoutAddress = "unknown"; + std::string operatorRewardAddress = "none"; + if (ExtractDestination(scriptPayout, dest)) { + payoutAddress = CBitcoinAddress(dest).ToString(); + } + if (ExtractDestination(scriptOperatorPayout, dest)) { + operatorRewardAddress = CBitcoinAddress(dest).ToString(); + } + + return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, " + "keyIDOwner=%s, pubKeyOperator=%s, keyIDVoting=%s, addr=%s, nProtocolVersion=%d, payoutAddress=%s, operatorRewardAddress=%s)", + nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason, + keyIDOwner.ToString(), pubKeyOperator.ToString(), keyIDVoting.ToString(), addr.ToStringIPPort(false), nProtocolVersion, payoutAddress, operatorRewardAddress); +} + +void CDeterministicMNState::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("registeredHeight", nRegisteredHeight)); + obj.push_back(Pair("lastPaidHeight", nLastPaidHeight)); + obj.push_back(Pair("PoSePenalty", nPoSePenalty)); + obj.push_back(Pair("PoSeRevivedHeight", nPoSeRevivedHeight)); + obj.push_back(Pair("PoSeBanHeight", nPoSeBanHeight)); + obj.push_back(Pair("revocationReason", nRevocationReason)); + obj.push_back(Pair("keyIDOwner", keyIDOwner.ToString())); + obj.push_back(Pair("pubKeyOperator", pubKeyOperator.ToString())); + obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString())); + obj.push_back(Pair("addr", addr.ToStringIPPort(false))); + obj.push_back(Pair("protocolVersion", nProtocolVersion)); + + CTxDestination dest; + if (ExtractDestination(scriptPayout, dest)) { + CBitcoinAddress bitcoinAddress(dest); + obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString())); + } + if (ExtractDestination(scriptOperatorPayout, dest)) { + CBitcoinAddress bitcoinAddress(dest); + obj.push_back(Pair("operatorRewardAddress", bitcoinAddress.ToString())); + } +} + +std::string CDeterministicMN::ToString() const +{ + return strprintf("CDeterministicMN(proTxHash=%s, nCollateralIndex=%d, nOperatorReward=%f, state=%s", proTxHash.ToString(), nCollateralIndex, (double)nOperatorReward / 100, pdmnState->ToString()); +} + +void CDeterministicMN::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + + UniValue stateObj; + pdmnState->ToJson(stateObj); + + obj.push_back(Pair("proTxHash", proTxHash.ToString())); + obj.push_back(Pair("collateralIndex", (int)nCollateralIndex)); + obj.push_back(Pair("operatorReward", (double)nOperatorReward / 100)); + obj.push_back(Pair("state", stateObj)); +} + +bool CDeterministicMNList::IsMNValid(const uint256& proTxHash) const +{ + auto p = mnMap.find(proTxHash); + if (p == nullptr) { + return false; + } + return IsMNValid(*p); +} + +bool CDeterministicMNList::IsMNPoSeBanned(const uint256& proTxHash) const +{ + auto p = mnMap.find(proTxHash); + if (p == nullptr) { + return false; + } + return IsMNPoSeBanned(*p); +} + +bool CDeterministicMNList::IsMNValid(const CDeterministicMNCPtr& dmn) const +{ + return !IsMNPoSeBanned(dmn); +} + +bool CDeterministicMNList::IsMNPoSeBanned(const CDeterministicMNCPtr& dmn) const +{ + assert(dmn); + const CDeterministicMNState& state = *dmn->pdmnState; + return state.nPoSeBanHeight != -1; +} + +CDeterministicMNCPtr CDeterministicMNList::GetMN(const uint256& proTxHash) const +{ + auto p = mnMap.find(proTxHash); + if (p == nullptr) { + return nullptr; + } + return *p; +} + +CDeterministicMNCPtr CDeterministicMNList::GetValidMN(const uint256& proTxHash) const +{ + auto dmn = GetMN(proTxHash); + if (dmn && !IsMNValid(dmn)) { + return nullptr; + } + return dmn; +} + +CDeterministicMNCPtr CDeterministicMNList::GetMNByOperatorKey(const CBLSPublicKey& pubKey) +{ + for (const auto& p : mnMap) { + if (p.second->pdmnState->pubKeyOperator == pubKey) { + return p.second; + } + } + return nullptr; +} + +static int CompareByLastPaid_GetHeight(const CDeterministicMN &dmn) +{ + int h = dmn.pdmnState->nLastPaidHeight; + if (dmn.pdmnState->nPoSeRevivedHeight != -1 && dmn.pdmnState->nPoSeRevivedHeight > h) { + h = dmn.pdmnState->nPoSeRevivedHeight; + } else if (h == 0) { + h = dmn.pdmnState->nRegisteredHeight; + } + return h; +} + +static bool CompareByLastPaid(const CDeterministicMN &_a, const CDeterministicMN &_b) +{ + int ah = CompareByLastPaid_GetHeight(_a); + int bh = CompareByLastPaid_GetHeight(_b); + if (ah == bh) { + return _a.proTxHash < _b.proTxHash; + } else { + return ah < bh; + } +} +static bool CompareByLastPaid(const CDeterministicMNCPtr &_a, const CDeterministicMNCPtr &_b) +{ + return CompareByLastPaid(*_a, *_b); +} + +CDeterministicMNCPtr CDeterministicMNList::GetMNPayee() const +{ + if (mnMap.size() == 0) + return nullptr; + + CDeterministicMNCPtr best; + ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) { + if (!best || CompareByLastPaid(dmn, best)) { + best = dmn; + } + }); + + return best; +} + +std::vector CDeterministicMNList::GetProjectedMNPayees(int nCount) const +{ + std::vector result; + result.reserve(nCount); + + CDeterministicMNList tmpMNList = *this; + for (int h = nHeight; h < nHeight + nCount; h++) { + tmpMNList.SetHeight(h); + + CDeterministicMNCPtr payee = tmpMNList.GetMNPayee(); + // push the original MN object instead of the one from the temporary list + result.push_back(GetMN(payee->proTxHash)); + + CDeterministicMNStatePtr newState = std::make_shared(*payee->pdmnState); + newState->nLastPaidHeight = h; + tmpMNList.UpdateMN(payee->proTxHash, newState); + } + + return result; +} + +CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNList& to) const +{ + CDeterministicMNListDiff diffRet; + diffRet.prevBlockHash = blockHash; + diffRet.blockHash = to.blockHash; + diffRet.nHeight = to.nHeight; + + for (const auto& p : to.mnMap) { + const auto fromPtr = mnMap.find(p.first); + if (fromPtr == nullptr) { + diffRet.addedMNs.emplace(p.first, p.second); + } else if (*p.second->pdmnState != *(*fromPtr)->pdmnState) { + diffRet.updatedMNs.emplace(p.first, p.second->pdmnState); + } + } + for (const auto& p : mnMap) { + const auto toPtr = to.mnMap.find(p.first); + if (toPtr == nullptr) { + diffRet.removedMns.insert(p.first); + } + } + + return diffRet; +} + +CDeterministicMNList CDeterministicMNList::ApplyDiff(const CDeterministicMNListDiff& diff) const +{ + assert(diff.prevBlockHash == blockHash && diff.nHeight == nHeight + 1); + + CDeterministicMNList result = *this; + result.blockHash = diff.blockHash; + result.nHeight = diff.nHeight; + + for (const auto& hash : diff.removedMns) { + result.RemoveMN(hash); + } + for (const auto& p : diff.addedMNs) { + result.AddMN(p.second); + } + for (const auto& p : diff.updatedMNs) { + result.UpdateMN(p.first, p.second); + } + + return result; +} + +void CDeterministicMNList::AddMN(const CDeterministicMNCPtr &dmn) +{ + assert(!mnMap.find(dmn->proTxHash)); + mnMap = mnMap.set(dmn->proTxHash, dmn); + AddUniqueProperty(dmn, dmn->pdmnState->addr); + AddUniqueProperty(dmn, dmn->pdmnState->keyIDOwner); + AddUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator); +} + +void CDeterministicMNList::UpdateMN(const uint256 &proTxHash, const CDeterministicMNStateCPtr &pdmnState) +{ + auto oldDmn = mnMap.find(proTxHash); + assert(oldDmn != nullptr); + auto dmn = std::make_shared(**oldDmn); + auto oldState = dmn->pdmnState; + dmn->pdmnState = pdmnState; + mnMap = mnMap.set(proTxHash, dmn); + + UpdateUniqueProperty(dmn, oldState->addr, pdmnState->addr); + UpdateUniqueProperty(dmn, oldState->keyIDOwner, pdmnState->keyIDOwner); + UpdateUniqueProperty(dmn, oldState->pubKeyOperator, pdmnState->pubKeyOperator); +} + +void CDeterministicMNList::RemoveMN(const uint256& proTxHash) +{ + auto dmn = GetMN(proTxHash); + assert(dmn != nullptr); + DeleteUniqueProperty(dmn, dmn->pdmnState->addr); + DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOwner); + DeleteUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator); + mnMap = mnMap.erase(proTxHash); +} + +CDeterministicMNManager::CDeterministicMNManager(CEvoDB& _evoDb) : + evoDb(_evoDb) +{ +} + +bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state) +{ + LOCK(cs); + + int nHeight = pindexPrev->nHeight + 1; + + CDeterministicMNList newList; + if (!BuildNewListFromBlock(block, pindexPrev, _state, newList)) { + return false; + } + + if (newList.GetHeight() == -1) { + newList.SetHeight(nHeight); + } + + newList.SetBlockHash(block.GetHash()); + + CDeterministicMNList oldList = GetListForBlock(pindexPrev->GetBlockHash()); + CDeterministicMNListDiff diff = oldList.BuildDiff(newList); + + evoDb.Write(std::make_pair(DB_LIST_DIFF, diff.blockHash), diff); + if ((nHeight % SNAPSHOT_LIST_PERIOD) == 0) { + evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, diff.blockHash), newList); + LogPrintf("CDeterministicMNManager::%s -- Wrote snapshot. nHeight=%d, mapCurMNs.allMNsCount=%d\n", + __func__, nHeight, newList.GetAllMNsCount()); + } + + if (nHeight == GetSpork15Value()) { + LogPrintf("CDeterministicMNManager::%s -- spork15 is active now. nHeight=%d\n", __func__, nHeight); + } + + CleanupCache(nHeight); + + return true; +} + +bool CDeterministicMNManager::UndoBlock(const CBlock& block, const CBlockIndex* pindex) +{ + LOCK(cs); + + int nHeight = pindex->nHeight; + uint256 blockHash = block.GetHash(); + + evoDb.Erase(std::make_pair(DB_LIST_DIFF, blockHash)); + evoDb.Erase(std::make_pair(DB_LIST_SNAPSHOT, blockHash)); + mnListsCache.erase(blockHash); + + if (nHeight == GetSpork15Value()) { + LogPrintf("CDeterministicMNManager::%s -- spork15 is not active anymore. nHeight=%d\n", __func__, nHeight); + } + + return true; +} + +void CDeterministicMNManager::UpdatedBlockTip(const CBlockIndex* pindex) +{ + LOCK(cs); + + tipHeight = pindex->nHeight; + tipBlockHash = pindex->GetBlockHash(); +} + +bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state, CDeterministicMNList& mnListRet) +{ + AssertLockHeld(cs); + + int nHeight = pindexPrev->nHeight + 1; + + CDeterministicMNList oldList = GetListForBlock(pindexPrev->GetBlockHash()); + CDeterministicMNList newList = oldList; + newList.SetBlockHash(uint256()); // we can't know the final block hash, so better not return a (invalid) block hash + newList.SetHeight(nHeight); + + auto payee = oldList.GetMNPayee(); + + // we skip the coinbase + for (int i = 1; i < (int)block.vtx.size(); i++) { + const CTransaction& tx = *block.vtx[i]; + + // check if any existing MN collateral is spent by this transaction + for (const auto& in : tx.vin) { + const uint256& proTxHash = in.prevout.hash; + auto dmn = newList.GetMN(proTxHash); + if (dmn && dmn->nCollateralIndex == in.prevout.n) { + newList.RemoveMN(proTxHash); + + LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. nHeight=%d, mapCurMNs.allMNsCount=%d\n", + __func__, proTxHash.ToString(), nHeight, newList.GetAllMNsCount()); + } + } + + if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { + CProRegTx proTx; + if (!GetTxPayload(tx, proTx)) { + assert(false); // this should have been handled already + } + + if (newList.HasUniqueProperty(proTx.addr)) + return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-addr"); + if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) + return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-key"); + + auto dmn = std::make_shared(tx.GetHash(), proTx); + + CDeterministicMNState dmnState = *dmn->pdmnState; + dmnState.nRegisteredHeight = nHeight; + + if (proTx.addr == CService() || proTx.nProtocolVersion == 0) { + // start in banned pdmnState as we need to wait for a ProUpServTx + dmnState.nPoSeBanHeight = nHeight; + } + + dmn->pdmnState = std::make_shared(dmnState); + + newList.AddMN(dmn); + + LogPrintf("CDeterministicMNManager::%s -- MN %s added at height %d: %s\n", + __func__, tx.GetHash().ToString(), nHeight, proTx.ToString()); + } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) { + CProUpServTx proTx; + if (!GetTxPayload(tx, proTx)) { + assert(false); // this should have been handled already + } + + if (newList.HasUniqueProperty(proTx.addr) && newList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTx.proTxHash) + return _state.DoS(100, false, REJECT_CONFLICT, "bad-protx-dup-addr"); + + CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); + if (!dmn) { + return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); + } + auto newState = std::make_shared(*dmn->pdmnState); + newState->addr = proTx.addr; + newState->nProtocolVersion = proTx.nProtocolVersion; + newState->scriptOperatorPayout = proTx.scriptOperatorPayout; + + if (newState->nPoSeBanHeight != -1) { + // only revive when all keys are set + if (newState->pubKeyOperator.IsValid() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) { + newState->nPoSeBanHeight = -1; + newState->nPoSeRevivedHeight = nHeight; + + LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n", + __func__, proTx.proTxHash.ToString(), nHeight); + } + } + + newList.UpdateMN(proTx.proTxHash, newState); + + LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n", + __func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString()); + } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { + CProUpRegTx proTx; + if (!GetTxPayload(tx, proTx)) { + assert(false); // this should have been handled already + } + + CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); + if (!dmn) { + return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); + } + auto newState = std::make_shared(*dmn->pdmnState); + if (newState->pubKeyOperator != proTx.pubKeyOperator) { + // reset all operator related fields and put MN into PoSe-banned state in case the operator key changes + newState->ResetOperatorFields(); + newState->BanIfNotBanned(nHeight); + } + newState->pubKeyOperator = proTx.pubKeyOperator; + newState->keyIDVoting = proTx.keyIDVoting; + newState->scriptPayout = proTx.scriptPayout; + + newList.UpdateMN(proTx.proTxHash, newState); + + LogPrintf("CDeterministicMNManager::%s -- MN %s updated at height %d: %s\n", + __func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString()); + } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { + CProUpRevTx proTx; + if (!GetTxPayload(tx, proTx)) { + assert(false); // this should have been handled already + } + + CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); + if (!dmn) { + return _state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); + } + auto newState = std::make_shared(*dmn->pdmnState); + newState->ResetOperatorFields(); + newState->BanIfNotBanned(nHeight); + newState->nRevocationReason = proTx.nReason; + + newList.UpdateMN(proTx.proTxHash, newState); + + LogPrintf("CDeterministicMNManager::%s -- MN %s revoked operator key at height %d: %s\n", + __func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString()); + } + } + + // The payee for the current block was determined by the previous block's list but it might have disappeared in the + // current block. We still pay that MN one last time however. + if (payee && newList.HasMN(payee->proTxHash)) { + auto newState = std::make_shared(*newList.GetMN(payee->proTxHash)->pdmnState); + newState->nLastPaidHeight = nHeight; + newList.UpdateMN(payee->proTxHash, newState); + } + + mnListRet = std::move(newList); + + return true; +} + +CDeterministicMNList CDeterministicMNManager::GetListForBlock(const uint256& blockHash) +{ + LOCK(cs); + + auto it = mnListsCache.find(blockHash); + if (it != mnListsCache.end()) { + return it->second; + } + + uint256 blockHashTmp = blockHash; + CDeterministicMNList snapshot; + std::list listDiff; + + while(true) { + // try using cache before reading from disk + it = mnListsCache.find(blockHashTmp); + if (it != mnListsCache.end()) { + snapshot = it->second; + break; + } + + if (evoDb.Read(std::make_pair(DB_LIST_SNAPSHOT, blockHashTmp), snapshot)) { + break; + } + + CDeterministicMNListDiff diff; + if (!evoDb.Read(std::make_pair(DB_LIST_DIFF, blockHashTmp), diff)) { + snapshot = CDeterministicMNList(blockHashTmp, -1); + break; + } + + listDiff.emplace_front(diff); + blockHashTmp = diff.prevBlockHash; + } + + for (const auto& diff : listDiff) { + if (diff.HasChanges()) { + snapshot = snapshot.ApplyDiff(diff); + } else { + snapshot.SetBlockHash(diff.blockHash); + snapshot.SetHeight(diff.nHeight); + } + } + + mnListsCache.emplace(blockHash, snapshot); + return snapshot; +} + +CDeterministicMNList CDeterministicMNManager::GetListAtChainTip() +{ + LOCK(cs); + return GetListForBlock(tipBlockHash); +} + +CDeterministicMNCPtr CDeterministicMNManager::GetMN(const uint256& blockHash, const uint256& proTxHash) +{ + auto mnList = GetListForBlock(blockHash); + return mnList.GetMN(proTxHash); +} + +bool CDeterministicMNManager::HasValidMNAtBlock(const uint256& blockHash, const uint256& proTxHash) +{ + auto mnList = GetListForBlock(blockHash); + return mnList.IsMNValid(proTxHash); +} + +bool CDeterministicMNManager::HasValidMNAtChainTip(const uint256& proTxHash) +{ + return GetListAtChainTip().IsMNValid(proTxHash); +} + +int64_t CDeterministicMNManager::GetSpork15Value() +{ + return sporkManager.GetSporkValue(SPORK_15_DETERMINISTIC_MNS_ENABLED); +} + +bool CDeterministicMNManager::IsDeterministicMNsSporkActive(int nHeight) +{ + LOCK(cs); + + if (nHeight == -1) { + nHeight = tipHeight; + } + + int64_t spork15Value = GetSpork15Value(); + return nHeight >= spork15Value; +} + +void CDeterministicMNManager::CleanupCache(int nHeight) +{ + AssertLockHeld(cs); + + std::vector toDelete; + for (const auto& p : mnListsCache) { + if (p.second.GetHeight() + LISTS_CACHE_SIZE < nHeight) { + toDelete.emplace_back(p.first); + } + } + for (const auto& h : toDelete) { + mnListsCache.erase(h); + } +} diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h new file mode 100644 index 000000000000..b3e2298b315c --- /dev/null +++ b/src/evo/deterministicmns.h @@ -0,0 +1,407 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ABSOLUTE_DETERMINISTICMNS_H +#define ABSOLUTE_DETERMINISTICMNS_H + +#include "evodb.h" +#include "providertx.h" +#include "dbwrapper.h" +#include "sync.h" +#include "bls/bls.h" + +#include "immer/map.hpp" +#include "immer/map_transient.hpp" + +#include + +class CBlock; +class CBlockIndex; +class CValidationState; + +class CDeterministicMNState +{ +public: + int nRegisteredHeight{-1}; + int nLastPaidHeight{0}; + int nPoSePenalty{0}; + int nPoSeRevivedHeight{-1}; + int nPoSeBanHeight{-1}; + uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED}; + + CKeyID keyIDOwner; + CBLSPublicKey pubKeyOperator; + CKeyID keyIDVoting; + CService addr; + int32_t nProtocolVersion; + CScript scriptPayout; + CScript scriptOperatorPayout; + +public: + CDeterministicMNState() {} + CDeterministicMNState(const CProRegTx& proTx) + { + keyIDOwner = proTx.keyIDOwner; + pubKeyOperator = proTx.pubKeyOperator; + keyIDVoting = proTx.keyIDVoting; + addr = proTx.addr; + nProtocolVersion = proTx.nProtocolVersion; + scriptPayout = proTx.scriptPayout; + } + template + CDeterministicMNState(deserialize_type, Stream& s) { s >> *this;} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nRegisteredHeight); + READWRITE(nLastPaidHeight); + READWRITE(nPoSePenalty); + READWRITE(nPoSeRevivedHeight); + READWRITE(nPoSeBanHeight); + READWRITE(nRevocationReason); + READWRITE(keyIDOwner); + READWRITE(pubKeyOperator); + READWRITE(keyIDVoting); + READWRITE(addr); + READWRITE(nProtocolVersion); + READWRITE(*(CScriptBase*)(&scriptPayout)); + READWRITE(*(CScriptBase*)(&scriptOperatorPayout)); + } + + void ResetOperatorFields() + { + pubKeyOperator = CBLSPublicKey(); + addr = CService(); + nProtocolVersion = 0; + scriptOperatorPayout = CScript(); + nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED; + } + void BanIfNotBanned(int height) + { + if (nPoSeBanHeight == -1) { + nPoSeBanHeight = height; + } + } + + bool operator==(const CDeterministicMNState& rhs) const + { + return nRegisteredHeight == rhs.nRegisteredHeight && + nLastPaidHeight == rhs.nLastPaidHeight && + nPoSePenalty == rhs.nPoSePenalty && + nPoSeRevivedHeight == rhs.nPoSeRevivedHeight && + nPoSeBanHeight == rhs.nPoSeBanHeight && + nRevocationReason == rhs.nRevocationReason && + keyIDOwner == rhs.keyIDOwner && + pubKeyOperator == rhs.pubKeyOperator && + keyIDVoting == rhs.keyIDVoting && + addr == rhs.addr && + nProtocolVersion == rhs.nProtocolVersion && + scriptPayout == rhs.scriptPayout && + scriptOperatorPayout == rhs.scriptOperatorPayout; + } + + bool operator!=(const CDeterministicMNState& rhs) const + { + return !(rhs == *this); + } + +public: + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; +typedef std::shared_ptr CDeterministicMNStatePtr; +typedef std::shared_ptr CDeterministicMNStateCPtr; + +class CDeterministicMN +{ +public: + CDeterministicMN() {} + CDeterministicMN(const uint256& _proTxHash, const CProRegTx& _proTx) + { + proTxHash = _proTxHash; + nCollateralIndex = _proTx.nCollateralIndex; + nOperatorReward = _proTx.nOperatorReward; + pdmnState = std::make_shared(_proTx); + } + template + CDeterministicMN(deserialize_type, Stream& s) { s >> *this;} + + uint256 proTxHash; + uint32_t nCollateralIndex; + uint16_t nOperatorReward; + CDeterministicMNStateCPtr pdmnState; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(proTxHash); + READWRITE(nCollateralIndex); + READWRITE(nOperatorReward); + READWRITE(pdmnState); + } + +public: + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; +typedef std::shared_ptr CDeterministicMNPtr; +typedef std::shared_ptr CDeterministicMNCPtr; + +class CDeterministicMNListDiff; + +template +void SerializeImmerMap(Stream& os, const immer::map& m) +{ + WriteCompactSize(os, m.size()); + for (typename immer::map::const_iterator mi = m.begin(); mi != m.end(); ++mi) + Serialize(os, (*mi)); +} + +template +void UnserializeImmerMap(Stream& is, immer::map& m) +{ + m = immer::map(); + unsigned int nSize = ReadCompactSize(is); + for (unsigned int i = 0; i < nSize; i++) + { + std::pair item; + Unserialize(is, item); + m = m.set(item.first, item.second); + } +} + +class CDeterministicMNList +{ +public: + typedef immer::map MnMap; + typedef immer::map> MnUniquePropertyMap; + +private: + uint256 blockHash; + int nHeight{-1}; + MnMap mnMap; + + // map of unique properties like address and keys + // we keep track of this as checking for duplicates would otherwise be painfully slow + // the entries in the map are ref counted as some properties might appear multiple times per MN (e.g. operator/owner keys) + MnUniquePropertyMap mnUniquePropertyMap; + +public: + CDeterministicMNList() {} + explicit CDeterministicMNList(const uint256& _blockHash, int _height) : + blockHash(_blockHash), + nHeight(_height) + {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(blockHash); + READWRITE(nHeight); + if (ser_action.ForRead()) { + UnserializeImmerMap(s, mnMap); + UnserializeImmerMap(s, mnUniquePropertyMap); + } else { + SerializeImmerMap(s, mnMap); + SerializeImmerMap(s, mnUniquePropertyMap); + } + } + +public: + + size_t GetAllMNsCount() const + { + return mnMap.size(); + } + + template + void ForEachMN(bool onlyValid, Callback&& cb) const { + for (const auto& p : mnMap) { + if (!onlyValid || IsMNValid(p.second)) { + cb(p.second); + } + } + } + +public: + const uint256& GetBlockHash() const + { + return blockHash; + } + void SetBlockHash(const uint256& _blockHash) + { + blockHash = _blockHash; + } + int GetHeight() const + { + return nHeight; + } + void SetHeight(int _height) + { + nHeight = _height; + } + + bool IsMNValid(const uint256& proTxHash) const; + bool IsMNPoSeBanned(const uint256& proTxHash) const; + + bool HasMN(const uint256& proTxHash) const { + return GetMN(proTxHash) != nullptr; + } + CDeterministicMNCPtr GetMN(const uint256& proTxHash) const; + CDeterministicMNCPtr GetValidMN(const uint256& proTxHash) const; + CDeterministicMNCPtr GetMNByOperatorKey(const CBLSPublicKey& pubKey); + CDeterministicMNCPtr GetMNPayee() const; + + /** + * Calculates the projected MN payees for the next *count* blocks. The result is not guaranteed to be correct + * as PoSe banning might occur later + * @param count + * @return + */ + std::vector GetProjectedMNPayees(int nCount) const; + + CDeterministicMNListDiff BuildDiff(const CDeterministicMNList& to) const; + CDeterministicMNList ApplyDiff(const CDeterministicMNListDiff& diff) const; + + void AddMN(const CDeterministicMNCPtr &dmn); + void UpdateMN(const uint256 &proTxHash, const CDeterministicMNStateCPtr &pdmnState); + void RemoveMN(const uint256& proTxHash); + + template + bool HasUniqueProperty(const T& v) const + { + return mnUniquePropertyMap.count(::SerializeHash(v)) != 0; + } + template + CDeterministicMNCPtr GetUniquePropertyMN(const T& v) const + { + auto p = mnUniquePropertyMap.find(::SerializeHash(v)); + if (!p) { + return nullptr; + } + return GetMN(p->first); + } + +private: + bool IsMNValid(const CDeterministicMNCPtr& dmn) const; + bool IsMNPoSeBanned(const CDeterministicMNCPtr& dmn) const; + + template + void AddUniqueProperty(const CDeterministicMNCPtr& dmn, const T& v) + { + auto hash = ::SerializeHash(v); + auto oldEntry = mnUniquePropertyMap.find(hash); + assert(!oldEntry || oldEntry->first == dmn->proTxHash); + std::pair newEntry(dmn->proTxHash, 1); + if (oldEntry) { + newEntry.second = oldEntry->second + 1; + } + mnUniquePropertyMap = mnUniquePropertyMap.set(hash, newEntry); + } + template + void DeleteUniqueProperty(const CDeterministicMNCPtr& dmn, const T& oldValue) + { + auto oldHash = ::SerializeHash(oldValue); + auto p = mnUniquePropertyMap.find(oldHash); + assert(p && p->first == dmn->proTxHash); + if (p->second == 1) { + mnUniquePropertyMap = mnUniquePropertyMap.erase(oldHash); + } else { + mnUniquePropertyMap = mnUniquePropertyMap.set(oldHash, std::make_pair(dmn->proTxHash, p->second - 1)); + } + } + template + void UpdateUniqueProperty(const CDeterministicMNCPtr& dmn, const T& oldValue, const T& newValue) + { + if (oldValue == newValue) { + return; + } + DeleteUniqueProperty(dmn, oldValue); + AddUniqueProperty(dmn, newValue); + } +}; + +class CDeterministicMNListDiff +{ +public: + uint256 prevBlockHash; + uint256 blockHash; + int nHeight{-1}; + std::map addedMNs; + std::map updatedMNs; + std::set removedMns; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(prevBlockHash); + READWRITE(blockHash); + READWRITE(nHeight); + READWRITE(addedMNs); + READWRITE(updatedMNs); + READWRITE(removedMns); + } + +public: + bool HasChanges() const + { + return !addedMNs.empty() || !updatedMNs.empty() || !removedMns.empty(); + } +}; + +class CDeterministicMNManager +{ + static const int SNAPSHOT_LIST_PERIOD = 576; // once per day + static const int LISTS_CACHE_SIZE = 576; + +public: + CCriticalSection cs; + +private: + CEvoDB& evoDb; + + std::map mnListsCache; + int tipHeight{-1}; + uint256 tipBlockHash; + +public: + CDeterministicMNManager(CEvoDB& _evoDb); + + bool ProcessBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& state); + bool UndoBlock(const CBlock& block, const CBlockIndex* pindex); + + void UpdatedBlockTip(const CBlockIndex *pindex); + + // the returned list will not contain the correct block hash (we can't know it yet as the coinbase TX is not updated yet) + bool BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& state, CDeterministicMNList& mnListRet); + + CDeterministicMNList GetListForBlock(const uint256& blockHash); + CDeterministicMNList GetListAtChainTip(); + + CDeterministicMNCPtr GetMN(const uint256& blockHash, const uint256& proTxHash); + bool HasValidMNAtBlock(const uint256& blockHash, const uint256& proTxHash); + bool HasValidMNAtChainTip(const uint256& proTxHash); + + bool IsDeterministicMNsSporkActive(int nHeight = -1); + +private: + void UpdateSpork15Value(); + int64_t GetSpork15Value(); + void CleanupCache(int nHeight); +}; + +extern CDeterministicMNManager* deterministicMNManager; + +#endif//ABSOLUTE_DETERMINISTICMNS_H diff --git a/src/evo/evodb.cpp b/src/evo/evodb.cpp new file mode 100644 index 000000000000..5b91c3d2b3f4 --- /dev/null +++ b/src/evo/evodb.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "evodb.h" + +CEvoDB* evoDb; + +CEvoDB::CEvoDB(size_t nCacheSize, bool fMemory, bool fWipe) : + db(fMemory ? "" : (GetDataDir() / "evodb"), nCacheSize, fMemory, fWipe), + dbTransaction(db) +{ +} diff --git a/src/evo/evodb.h b/src/evo/evodb.h new file mode 100644 index 000000000000..9d0dd1c12467 --- /dev/null +++ b/src/evo/evodb.h @@ -0,0 +1,64 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ABSOLUTE_EVODB_H +#define ABSOLUTE_EVODB_H + +#include "dbwrapper.h" +#include "sync.h" + +class CEvoDB +{ +private: + CCriticalSection cs; + CDBWrapper db; + CDBTransaction dbTransaction; + +public: + CEvoDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); + + std::unique_ptr BeginTransaction() + { + LOCK(cs); + auto t = CScopedDBTransaction::Begin(dbTransaction); + return t; + } + + template + bool Read(const K& key, V& value) + { + LOCK(cs); + return dbTransaction.Read(key, value); + } + + template + void Write(const K& key, const V& value) + { + LOCK(cs); + dbTransaction.Write(key, value); + } + + template + bool Exists(const K& key) + { + LOCK(cs); + return dbTransaction.Exists(key); + } + + template + void Erase(const K& key) + { + LOCK(cs); + dbTransaction.Erase(key); + } + + CDBWrapper& GetRawDB() + { + return db; + } +}; + +extern CEvoDB* evoDb; + +#endif//ABSOLUTE_EVODB_H diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp new file mode 100644 index 000000000000..c360d512d596 --- /dev/null +++ b/src/evo/providertx.cpp @@ -0,0 +1,384 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "providertx.h" +#include "specialtx.h" +#include "deterministicmns.h" + +#include "hash.h" +#include "clientversion.h" +#include "streams.h" +#include "messagesigner.h" +#include "chainparams.h" +#include "validation.h" +#include "univalue.h" +#include "core_io.h" +#include "script/standard.h" +#include "base58.h" + +template +static bool CheckService(const uint256& proTxHash, const ProTx& proTx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + if (proTx.nProtocolVersion < MIN_PROTX_PROTO_VERSION || proTx.nProtocolVersion > MAX_PROTX_PROTO_VERSION) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-proto-version"); + + if (!proTx.addr.IsValid()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); + if (Params().NetworkIDString() != CBaseChainParams::REGTEST && !proTx.addr.IsRoutable()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); + + if (!proTx.addr.IsIPv4()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); + + if (pindexPrev) { + auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); + if (mnList.HasUniqueProperty(proTx.addr) && mnList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTxHash) { + return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr"); + } + } + + return true; +} + +template +static bool CheckSig(const ProTx& proTx, const CKeyID& keyID, CValidationState& state) +{ + std::string strError; + if (!CHashSigner::VerifyHash(::SerializeHash(proTx), keyID, proTx.vchSig, strError)) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError); + return true; +} + +template +static bool CheckSig(const ProTx& proTx, const CBLSPublicKey& pubKey, CValidationState& state) +{ + if (!proTx.sig.VerifyInsecure(pubKey, ::SerializeHash(proTx))) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false); + return true; +} + +template +static bool CheckInputsHashAndSig(const CTransaction &tx, const ProTx& proTx, const PubKey& pubKey, CValidationState& state) +{ + uint256 inputsHash = CalcTxInputsHash(tx); + if (inputsHash != proTx.inputsHash) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-inputs-hash"); + + if (!CheckSig(proTx, pubKey, state)) + return false; + + return true; +} + +bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + AssertLockHeld(cs_main); + + CProRegTx ptx; + if (!GetTxPayload(tx, ptx)) + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + + if (ptx.nVersion > CProRegTx::CURRENT_VERSION) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); + + if (ptx.nCollateralIndex >= tx.vout.size()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-index"); + if (tx.vout[ptx.nCollateralIndex].nValue != 1000 * COIN) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral"); + if (ptx.keyIDOwner.IsNull() || !ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); + // we may support P2SH later, but restrict it for now (while in transitioning phase from old MN list to deterministic list) + if (!ptx.scriptPayout.IsPayToPublicKeyHash()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); + + CTxDestination payoutDest; + if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { + // should not happen as we checked script types before + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest"); + } + // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) + if (payoutDest == CTxDestination(ptx.keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDVoting)) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse"); + } + + // This is a temporary restriction that will be lifted later + // It is required while we are transitioning from the old MN list to the deterministic list + if (tx.vout[ptx.nCollateralIndex].scriptPubKey != ptx.scriptPayout) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-collateral"); + + // It's allowed to set addr/protocolVersion to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later + // If any of both is set, it must be valid however + if ((ptx.addr != CService() || ptx.nProtocolVersion != 0) && !CheckService(tx.GetHash(), ptx, pindexPrev, state)) + return false; + + if (ptx.nOperatorReward > 10000) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-reward"); + + if (pindexPrev) { + auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); + if (mnList.HasUniqueProperty(ptx.keyIDOwner) || mnList.HasUniqueProperty(ptx.pubKeyOperator)) { + return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key"); + } + + if (!deterministicMNManager->IsDeterministicMNsSporkActive(pindexPrev->nHeight)) { + if (ptx.keyIDOwner != ptx.keyIDVoting) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same"); + } + } + } + + if (!CheckInputsHashAndSig(tx, ptx, ptx.keyIDOwner, state)) + return false; + return true; +} + +bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + AssertLockHeld(cs_main); + + CProUpServTx ptx; + if (!GetTxPayload(tx, ptx)) + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + + if (ptx.nVersion > CProRegTx::CURRENT_VERSION) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); + + if (!CheckService(ptx.proTxHash, ptx, pindexPrev, state)) + return false; + + if (pindexPrev) { + auto mn = deterministicMNManager->GetMN(pindexPrev->GetBlockHash(), ptx.proTxHash); + if (!mn) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); + + if (ptx.scriptOperatorPayout != CScript()) { + if (mn->nOperatorReward == 0) { + // don't allow to set operator reward payee in case no operatorReward was set + return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); + } + // we may support P2SH later, but restrict it for now (while in transitioning phase from old MN list to deterministic list) + if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); + } + + // we can only check the signature if pindexPrev != NULL and the MN is known + if (!CheckInputsHashAndSig(tx, ptx, mn->pdmnState->pubKeyOperator, state)) + return false; + } + + return true; +} + +bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + AssertLockHeld(cs_main); + + CProUpRegTx ptx; + if (!GetTxPayload(tx, ptx)) + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + + if (ptx.nVersion > CProRegTx::CURRENT_VERSION) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); + + if (!ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); + // we may support P2SH later, but restrict it for now (while in transitioning phase from old MN list to deterministic list) + if (!ptx.scriptPayout.IsPayToPublicKeyHash()) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); + + CTxDestination payoutDest; + if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { + // should not happen as we checked script types before + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest"); + } + + if (pindexPrev) { + auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); + auto dmn = mnList.GetMN(ptx.proTxHash); + if (!dmn) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); + + // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) + if (payoutDest == CTxDestination(dmn->pdmnState->keyIDOwner) || payoutDest == CTxDestination(ptx.keyIDVoting)) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-reuse"); + } + + // This is a temporary restriction that will be lifted later + // It is required while we are transitioning from the old MN list to the deterministic list + Coin coin; + if (!GetUTXOCoin(COutPoint(dmn->proTxHash, dmn->nCollateralIndex), coin) || coin.IsSpent()) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-payee-collateral"); + } + if (coin.out.scriptPubKey != ptx.scriptPayout) + return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-collateral"); + + if (mnList.HasUniqueProperty(ptx.pubKeyOperator)) { + auto otherDmn = mnList.GetUniquePropertyMN(ptx.pubKeyOperator); + if (ptx.proTxHash != otherDmn->proTxHash) { + return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-key"); + } + } + + if (!deterministicMNManager->IsDeterministicMNsSporkActive(pindexPrev->nHeight)) { + if (dmn->pdmnState->keyIDOwner != ptx.keyIDVoting) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-not-same"); + } + } + + if (!CheckInputsHashAndSig(tx, ptx, dmn->pdmnState->keyIDOwner, state)) + return false; + } + + return true; +} + +bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + AssertLockHeld(cs_main); + + CProUpRevTx ptx; + if (!GetTxPayload(tx, ptx)) + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + + if (ptx.nVersion > CProRegTx::CURRENT_VERSION) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); + + // ptx.nReason < CProUpRevTx::REASON_NOT_SPECIFIED is always `false` since + // ptx.nReason is unsigned and CProUpRevTx::REASON_NOT_SPECIFIED == 0 + if (ptx.nReason > CProUpRevTx::REASON_LAST) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-reason"); + + if (pindexPrev) { + auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); + auto dmn = mnList.GetMN(ptx.proTxHash); + if (!dmn) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); + + if (!CheckInputsHashAndSig(tx, ptx, dmn->pdmnState->pubKeyOperator, state)) + return false; + } + + return true; +} + +std::string CProRegTx::ToString() const +{ + CTxDestination dest; + std::string payee = "unknown"; + if (ExtractDestination(scriptPayout, dest)) { + payee = CBitcoinAddress(dest).ToString(); + } + + return strprintf("CProRegTx(nVersion=%d, nProtocolVersion=%d, nCollateralIndex=%d, addr=%s, nOperatorReward=%f, keyIDOwner=%s, pubKeyOperator=%s, keyIDVoting=%s, scriptPayout=%s)", + nVersion, nProtocolVersion, nCollateralIndex, addr.ToString(), (double)nOperatorReward / 100, keyIDOwner.ToString(), pubKeyOperator.ToString(), keyIDVoting.ToString(), payee); +} + +void CProRegTx::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("version", nVersion)); + obj.push_back(Pair("protocolVersion", nProtocolVersion)); + obj.push_back(Pair("collateralIndex", (int)nCollateralIndex)); + obj.push_back(Pair("service", addr.ToString(false))); + obj.push_back(Pair("keyIDOwner", keyIDOwner.ToString())); + obj.push_back(Pair("pubKeyOperator", pubKeyOperator.ToString())); + obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString())); + + CTxDestination dest; + if (ExtractDestination(scriptPayout, dest)) { + CBitcoinAddress bitcoinAddress(dest); + obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString())); + } + obj.push_back(Pair("operatorReward", (double)nOperatorReward / 100)); + + obj.push_back(Pair("inputsHash", inputsHash.ToString())); +} + +std::string CProUpServTx::ToString() const +{ + CTxDestination dest; + std::string payee = "unknown"; + if (ExtractDestination(scriptOperatorPayout, dest)) { + payee = CBitcoinAddress(dest).ToString(); + } + + return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, nProtocolVersion=%d, addr=%s, operatorPayoutAddress=%s)", + nVersion, proTxHash.ToString(), nProtocolVersion, addr.ToString(), payee); +} + +void CProUpServTx::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("version", nVersion)); + obj.push_back(Pair("proTxHash", proTxHash.ToString())); + obj.push_back(Pair("protocolVersion", nProtocolVersion)); + obj.push_back(Pair("service", addr.ToString(false))); + CTxDestination dest; + if (ExtractDestination(scriptOperatorPayout, dest)) { + CBitcoinAddress bitcoinAddress(dest); + obj.push_back(Pair("operatorPayoutAddress", bitcoinAddress.ToString())); + } + obj.push_back(Pair("inputsHash", inputsHash.ToString())); +} + +std::string CProUpRegTx::ToString() const +{ + CTxDestination dest; + std::string payee = "unknown"; + if (ExtractDestination(scriptPayout, dest)) { + payee = CBitcoinAddress(dest).ToString(); + } + + return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, keyIDVoting=%s, payoutAddress=%s)", + nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), keyIDVoting.ToString(), payee); +} + +void CProUpRegTx::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("version", nVersion)); + obj.push_back(Pair("proTxHash", proTxHash.ToString())); + obj.push_back(Pair("pubKeyOperator", pubKeyOperator.ToString())); + obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString())); + CTxDestination dest; + if (ExtractDestination(scriptPayout, dest)) { + CBitcoinAddress bitcoinAddress(dest); + obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString())); + } + obj.push_back(Pair("inputsHash", inputsHash.ToString())); +} + +std::string CProUpRevTx::ToString() const +{ + return strprintf("CProUpRevTx(nVersion=%d, proTxHash=%s, nReason=%d)", + nVersion, proTxHash.ToString(), nReason); +} + +void CProUpRevTx::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("version", nVersion)); + obj.push_back(Pair("proTxHash", proTxHash.ToString())); + obj.push_back(Pair("reason", (int)nReason)); + obj.push_back(Pair("inputsHash", inputsHash.ToString())); +} + +bool IsProTxCollateral(const CTransaction& tx, uint32_t n) +{ + return GetProTxCollateralIndex(tx) == n; +} + +uint32_t GetProTxCollateralIndex(const CTransaction& tx) +{ + if (tx.nVersion < 3 || tx.nType != TRANSACTION_PROVIDER_REGISTER) + return (uint32_t) - 1; + CProRegTx proTx; + if (!GetTxPayload(tx, proTx)) + assert(false); + return proTx.nCollateralIndex; +} diff --git a/src/evo/providertx.h b/src/evo/providertx.h new file mode 100644 index 000000000000..55519cdf6620 --- /dev/null +++ b/src/evo/providertx.h @@ -0,0 +1,183 @@ +// Copyright (c) 2018 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef Absolute_PROVIDERTX_H +#define Absolute_PROVIDERTX_H + +#include "primitives/transaction.h" +#include "consensus/validation.h" +#include "bls/bls.h" + +#include "netaddress.h" +#include "pubkey.h" + +class CBlockIndex; +class UniValue; + +class CProRegTx +{ +public: + static const uint16_t CURRENT_VERSION = 1; + +public: + uint16_t nVersion{CURRENT_VERSION}; // message version + int32_t nProtocolVersion{0}; + uint32_t nCollateralIndex{(uint32_t) - 1}; + CService addr; + CKeyID keyIDOwner; + CBLSPublicKey pubKeyOperator; + CKeyID keyIDVoting; + uint16_t nOperatorReward{0}; + CScript scriptPayout; + uint256 inputsHash; // replay protection + std::vector vchSig; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(nProtocolVersion); + READWRITE(nCollateralIndex); + READWRITE(addr); + READWRITE(keyIDOwner); + READWRITE(pubKeyOperator); + READWRITE(keyIDVoting); + READWRITE(*(CScriptBase*)(&scriptPayout)); + READWRITE(nOperatorReward); + READWRITE(inputsHash); + if (!(s.GetType() & SER_GETHASH)) { + READWRITE(vchSig); + } + } + + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + +class CProUpServTx +{ +public: + static const uint16_t CURRENT_VERSION = 1; + +public: + uint16_t nVersion{CURRENT_VERSION}; // message version + uint256 proTxHash; + int32_t nProtocolVersion{0}; + CService addr; + CScript scriptOperatorPayout; + uint256 inputsHash; // replay protection + CBLSSignature sig; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(proTxHash); + READWRITE(nProtocolVersion); + READWRITE(addr); + READWRITE(*(CScriptBase*)(&scriptOperatorPayout)); + READWRITE(inputsHash); + if (!(s.GetType() & SER_GETHASH)) { + READWRITE(sig); + } + } + +public: + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + +class CProUpRegTx +{ +public: + static const uint16_t CURRENT_VERSION = 1; + +public: + uint16_t nVersion{CURRENT_VERSION}; // message version + uint256 proTxHash; + CBLSPublicKey pubKeyOperator; + CKeyID keyIDVoting; + CScript scriptPayout; + uint256 inputsHash; // replay protection + std::vector vchSig; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(proTxHash); + READWRITE(pubKeyOperator); + READWRITE(keyIDVoting); + READWRITE(*(CScriptBase*)(&scriptPayout)); + READWRITE(inputsHash); + if (!(s.GetType() & SER_GETHASH)) { + READWRITE(vchSig); + } + } + +public: + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + +class CProUpRevTx +{ +public: + static const uint16_t CURRENT_VERSION = 1; + + // these are just informational and do not have any effect on the revocation + enum { + REASON_NOT_SPECIFIED = 0, + REASON_TERMINATION_OF_SERVICE = 1, + REASON_COMPROMISED_KEYS = 2, + REASON_CHANGE_OF_KEYS = 3, + REASON_LAST = REASON_CHANGE_OF_KEYS + }; + +public: + uint16_t nVersion{CURRENT_VERSION}; // message version + uint256 proTxHash; + uint16_t nReason{REASON_NOT_SPECIFIED}; + uint256 inputsHash; // replay protection + CBLSSignature sig; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(proTxHash); + READWRITE(nReason); + READWRITE(inputsHash); + if (!(s.GetType() & SER_GETHASH)) { + READWRITE(sig); + } + } + +public: + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + + +bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); +bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); +bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); +bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); + +bool IsProTxCollateral(const CTransaction& tx, uint32_t n); +uint32_t GetProTxCollateralIndex(const CTransaction& tx); + +#endif//ABSOLUTE_PROVIDERTX_H diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp new file mode 100644 index 000000000000..3b590c9ecf7e --- /dev/null +++ b/src/evo/simplifiedmns.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "simplifiedmns.h" +#include "specialtx.h" +#include "deterministicmns.h" +#include "cbtx.h" + +#include "validation.h" +#include "univalue.h" +#include "consensus/merkle.h" +#include "chainparams.h" + +CSimplifiedMNListEntry::CSimplifiedMNListEntry(const CDeterministicMN& dmn) : + proRegTxHash(dmn.proTxHash), + service(dmn.pdmnState->addr), + pubKeyOperator(dmn.pdmnState->pubKeyOperator), + keyIDVoting(dmn.pdmnState->keyIDVoting), + isValid(dmn.pdmnState->nPoSeBanHeight == -1) +{ +} + +uint256 CSimplifiedMNListEntry::CalcHash() const +{ + CHashWriter hw(SER_GETHASH, CLIENT_VERSION); + hw << *this; + return hw.GetHash(); +} + +std::string CSimplifiedMNListEntry::ToString() const +{ + return strprintf("CSimplifiedMNListEntry(proRegTxHash=%s, service=%s, pubKeyOperator=%s, keyIDVoting=%s, isValie=%d)", + proRegTxHash.ToString(), service.ToString(false), pubKeyOperator.ToString(), keyIDVoting.ToString(), isValid); +} + +void CSimplifiedMNListEntry::ToJson(UniValue& obj) const +{ + obj.clear(); + obj.setObject(); + obj.push_back(Pair("proRegTxHash", proRegTxHash.ToString())); + obj.push_back(Pair("service", service.ToString(false))); + obj.push_back(Pair("pubKeyOperator", pubKeyOperator.ToString())); + obj.push_back(Pair("keyIDVoting", keyIDVoting.ToString())); + obj.push_back(Pair("isValid", isValid)); +} + +CSimplifiedMNList::CSimplifiedMNList(const std::vector& smlEntries) +{ + mnList = smlEntries; + + std::sort(mnList.begin(), mnList.end(), [&](const CSimplifiedMNListEntry& a, const CSimplifiedMNListEntry& b) { + return a.proRegTxHash.Compare(b.proRegTxHash) < 0; + }); +} + +CSimplifiedMNList::CSimplifiedMNList(const CDeterministicMNList& dmnList) +{ + mnList.reserve(dmnList.GetAllMNsCount()); + + dmnList.ForEachMN(false, [this](const CDeterministicMNCPtr& dmn) { + mnList.emplace_back(*dmn); + }); + + std::sort(mnList.begin(), mnList.end(), [&](const CSimplifiedMNListEntry& a, const CSimplifiedMNListEntry& b) { + return a.proRegTxHash.Compare(b.proRegTxHash) < 0; + }); +} + +uint256 CSimplifiedMNList::CalcMerkleRoot(bool *pmutated) const +{ + std::vector leaves; + leaves.reserve(mnList.size()); + for (const auto& e : mnList) { + leaves.emplace_back(e.CalcHash()); + } + return ComputeMerkleRoot(leaves, pmutated); +} + +void CSimplifiedMNListDiff::ToJson(UniValue& obj) const +{ + obj.setObject(); + + obj.push_back(Pair("baseBlockHash", baseBlockHash.ToString())); + obj.push_back(Pair("blockHash", blockHash.ToString())); + + UniValue deletedMNsArr(UniValue::VARR); + for (const auto& h : deletedMNs) { + deletedMNsArr.push_back(h.ToString()); + } + obj.push_back(Pair("deletedMNs", deletedMNsArr)); + + UniValue mnListArr(UniValue::VARR); + for (const auto& e : mnList) { + UniValue eObj; + e.ToJson(eObj); + mnListArr.push_back(eObj); + } + obj.push_back(Pair("mnList", mnListArr)); + + CCbTx cbTxPayload; + if (GetTxPayload(*cbTx, cbTxPayload)) { + obj.push_back(Pair("merkleRootMNList", cbTxPayload.merkleRootMNList.ToString())); + } +} + +bool BuildSimplifiedMNListDiff(const uint256& baseBlockHash, const uint256& blockHash, CSimplifiedMNListDiff& mnListDiffRet, std::string& errorRet) +{ + AssertLockHeld(cs_main); + mnListDiffRet = CSimplifiedMNListDiff(); + + BlockMap::iterator baseBlockIt = mapBlockIndex.begin(); + if (!baseBlockHash.IsNull()) { + baseBlockIt = mapBlockIndex.find(baseBlockHash); + } + auto blockIt = mapBlockIndex.find(blockHash); + if (baseBlockIt == mapBlockIndex.end()) { + errorRet = strprintf("block %s not found", baseBlockHash.ToString()); + return false; + } + if (blockIt == mapBlockIndex.end()) { + errorRet = strprintf("block %s not found", blockHash.ToString()); + return false; + } + + if (!chainActive.Contains(baseBlockIt->second) || !chainActive.Contains(blockIt->second)) { + errorRet = strprintf("block %s and %s are not in the same chain", baseBlockHash.ToString(), blockHash.ToString()); + return false; + } + if (baseBlockIt->second->nHeight > blockIt->second->nHeight) { + errorRet = strprintf("base block %s is higher then block %s", baseBlockHash.ToString(), blockHash.ToString()); + return false; + } + + LOCK(deterministicMNManager->cs); + + auto baseDmnList = deterministicMNManager->GetListForBlock(baseBlockHash); + auto dmnList = deterministicMNManager->GetListForBlock(blockHash); + auto dmnDiff = baseDmnList.BuildDiff(dmnList); + + // TODO store coinbase TX in CBlockIndex + CBlock block; + if (!ReadBlockFromDisk(block, blockIt->second, Params().GetConsensus())) { + errorRet = strprintf("failed to read block %s from disk", blockHash.ToString()); + return false; + } + + mnListDiffRet.baseBlockHash = baseBlockHash; + mnListDiffRet.blockHash = blockHash; + mnListDiffRet.cbTx = block.vtx[0]; + + std::vector vHashes; + std::vector vMatch(block.vtx.size(), false); + for (const auto& tx : block.vtx) { + vHashes.emplace_back(tx->GetHash()); + } + vMatch[0] = true; // only coinbase matches + mnListDiffRet.cbTxMerkleTree = CPartialMerkleTree(vHashes, vMatch); + mnListDiffRet.deletedMNs.assign(dmnDiff.removedMns.begin(), dmnDiff.removedMns.end()); + + for (const auto& p : dmnDiff.addedMNs) { + mnListDiffRet.mnList.emplace_back(*p.second); + } + for (const auto& p : dmnDiff.updatedMNs) { + const auto& dmn = dmnList.GetMN(p.first); + CDeterministicMN newDmn(*dmn); + newDmn.pdmnState = p.second; + mnListDiffRet.mnList.emplace_back(newDmn); + } + + return true; +} diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h new file mode 100644 index 000000000000..3234e6408ee6 --- /dev/null +++ b/src/evo/simplifiedmns.h @@ -0,0 +1,113 @@ +// Copyright (c) 2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ABSOLUTE_SIMPLIFIEDMNS_H +#define ABSOLUTE_SIMPLIFIEDMNS_H + +#include "serialize.h" +#include "pubkey.h" +#include "netaddress.h" +#include "merkleblock.h" +#include "bls/bls.h" + +class UniValue; +class CDeterministicMNList; +class CDeterministicMN; + +class CSimplifiedMNListEntry +{ +public: + uint256 proRegTxHash; + CService service; + CBLSPublicKey pubKeyOperator; + CKeyID keyIDVoting; + bool isValid; + +public: + CSimplifiedMNListEntry() {} + CSimplifiedMNListEntry(const CDeterministicMN& dmn); + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(proRegTxHash); + READWRITE(service); + READWRITE(pubKeyOperator); + READWRITE(keyIDVoting); + READWRITE(isValid); + } + +public: + uint256 CalcHash() const; + + std::string ToString() const; + void ToJson(UniValue& obj) const; +}; + +class CSimplifiedMNList +{ +public: + std::vector mnList; + +public: + CSimplifiedMNList() {} + CSimplifiedMNList(const std::vector& smlEntries); + CSimplifiedMNList(const CDeterministicMNList& dmnList); + + uint256 CalcMerkleRoot(bool *pmutated = NULL) const; +}; + +/// P2P messages + +class CGetSimplifiedMNListDiff +{ +public: + uint256 baseBlockHash; + uint256 blockHash; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(baseBlockHash); + READWRITE(blockHash); + } +}; + +class CSimplifiedMNListDiff +{ +public: + uint256 baseBlockHash; + uint256 blockHash; + CPartialMerkleTree cbTxMerkleTree; + CTransactionRef cbTx; + std::vector deletedMNs; + std::vector mnList; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(baseBlockHash); + READWRITE(blockHash); + READWRITE(cbTxMerkleTree); + READWRITE(cbTx); + READWRITE(deletedMNs); + READWRITE(mnList); + } + +public: + void ToJson(UniValue& obj) const; +}; + +bool BuildSimplifiedMNListDiff(const uint256& baseBlockHash, const uint256& blockHash, CSimplifiedMNListDiff& mnListDiffRet, std::string& errorRet); + +#endif//ABSOLUTE_SIMPLIFIEDMNS_H diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp new file mode 100644 index 000000000000..bcb1ce0c8130 --- /dev/null +++ b/src/evo/specialtx.cpp @@ -0,0 +1,120 @@ +// Copyright (c) 2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "primitives/transaction.h" +#include "primitives/block.h" +#include "consensus/validation.h" +#include "hash.h" +#include "clientversion.h" +#include "validation.h" +#include "chainparams.h" + +#include "specialtx.h" +#include "deterministicmns.h" +#include "cbtx.h" + +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + AssertLockHeld(cs_main); + + if (tx.nVersion < 3 || tx.nType == TRANSACTION_NORMAL) + return true; + + if (pindexPrev && VersionBitsState(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_AIP0003, versionbitscache) != THRESHOLD_ACTIVE) { + return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); + } + + switch (tx.nType) { + case TRANSACTION_PROVIDER_REGISTER: + return CheckProRegTx(tx, pindexPrev, state); + case TRANSACTION_PROVIDER_UPDATE_SERVICE: + return CheckProUpServTx(tx, pindexPrev, state); + case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: + return CheckProUpRegTx(tx, pindexPrev, state); + case TRANSACTION_PROVIDER_UPDATE_REVOKE: + return CheckProUpRevTx(tx, pindexPrev, state); + case TRANSACTION_COINBASE: + return CheckCbTx(tx, pindexPrev, state); + } + + return state.DoS(10, false, REJECT_INVALID, "bad-tx-type"); +} + +bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValidationState& state) +{ + if (tx.nVersion < 3 || tx.nType == TRANSACTION_NORMAL) + return true; + + switch (tx.nType) { + case TRANSACTION_PROVIDER_REGISTER: + case TRANSACTION_PROVIDER_UPDATE_SERVICE: + case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: + case TRANSACTION_PROVIDER_UPDATE_REVOKE: + return true; // handled in batches per block + case TRANSACTION_COINBASE: + return true; // nothing to do + } + + return state.DoS(100, false, REJECT_INVALID, "bad-tx-type"); +} + +bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) +{ + if (tx.nVersion < 3 || tx.nType == TRANSACTION_NORMAL) + return true; + + switch (tx.nType) { + case TRANSACTION_PROVIDER_REGISTER: + case TRANSACTION_PROVIDER_UPDATE_SERVICE: + case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: + case TRANSACTION_PROVIDER_UPDATE_REVOKE: + return true; // handled in batches per block + case TRANSACTION_COINBASE: + return true; // nothing to do + } + + return false; +} + +bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state) +{ + for (int i = 0; i < (int)block.vtx.size(); i++) { + const CTransaction& tx = *block.vtx[i]; + if (!CheckSpecialTx(tx, pindex->pprev, state)) + return false; + if (!ProcessSpecialTx(tx, pindex, state)) + return false; + } + + if (!deterministicMNManager->ProcessBlock(block, pindex->pprev, state)) + return false; + + if (!CheckCbTxMerkleRootMNList(block, pindex, state)) + return false; + + return true; +} + +bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex) +{ + for (int i = (int)block.vtx.size() - 1; i >= 0; --i) { + const CTransaction& tx = *block.vtx[i]; + if (!UndoSpecialTx(tx, pindex)) + return false; + } + + if (!deterministicMNManager->UndoBlock(block, pindex)) + return false; + + return true; +} + +uint256 CalcTxInputsHash(const CTransaction& tx) +{ + CHashWriter hw(CLIENT_VERSION, SER_GETHASH); + for (const auto& in : tx.vin) { + hw << in.prevout; + } + return hw.GetHash(); +} diff --git a/src/evo/specialtx.h b/src/evo/specialtx.h new file mode 100644 index 000000000000..0c6a7807f765 --- /dev/null +++ b/src/evo/specialtx.h @@ -0,0 +1,52 @@ +// Copyright (c) 2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef ABSOLUTE_SPECIALTX_H +#define ABSOLUTE_SPECIALTX_H + +#include "streams.h" +#include "version.h" +#include "primitives/transaction.h" + +class CBlock; +class CBlockIndex; +class CValidationState; + +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); +bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state); +bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex); + +template +inline bool GetTxPayload(const std::vector& payload, T& obj) +{ + CDataStream ds(payload, SER_NETWORK, PROTOCOL_VERSION); + try { + ds >> obj; + } catch (std::exception& e) { + return false; + } + return ds.empty(); +} +template +inline bool GetTxPayload(const CMutableTransaction& tx, T& obj) +{ + return GetTxPayload(tx.vExtraPayload, obj); +} +template +inline bool GetTxPayload(const CTransaction& tx, T& obj) +{ + return GetTxPayload(tx.vExtraPayload, obj); +} + +template +void SetTxPayload(CMutableTransaction& tx, const T& payload) +{ + CDataStream ds(SER_NETWORK, PROTOCOL_VERSION); + ds << payload; + tx.vExtraPayload.assign(ds.begin(), ds.end()); +} + +uint256 CalcTxInputsHash(const CTransaction& tx); + +#endif//ABSOLUTE_SPECIALTX_H diff --git a/src/flat-database.h b/src/flat-database.h index e661598a7fcf..de8666535d46 100644 --- a/src/flat-database.h +++ b/src/flat-database.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/governance-classes.cpp b/src/governance-classes.cpp index e9a85f580343..08c822cb9a14 100644 --- a/src/governance-classes.cpp +++ b/src/governance-classes.cpp @@ -1,15 +1,15 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. //#define ENABLE_ABSOLUTE_DEBUG -#include "core_io.h" #include "governance-classes.h" +#include "core_io.h" #include "init.h" -#include "validation.h" #include "utilstrencodings.h" +#include "validation.h" #include @@ -25,19 +25,19 @@ std::vector SplitBy(const std::string& strCommand, const std::strin std::vector vParts; boost::split(vParts, strCommand, boost::is_any_of(strDelimit)); - for(int q=0; q<(int)vParts.size(); q++) { - if(strDelimit.find(vParts[q]) != std::string::npos) { - vParts.erase(vParts.begin()+q); + for (int q = 0; q < (int)vParts.size(); q++) { + if (strDelimit.find(vParts[q]) != std::string::npos) { + vParts.erase(vParts.begin() + q); --q; } } - return vParts; + return vParts; } CAmount ParsePaymentAmount(const std::string& strAmount) { - DBG( std::cout << "ParsePaymentAmount Start: strAmount = " << strAmount << std::endl; ); + DBG(std::cout << "ParsePaymentAmount Start: strAmount = " << strAmount << std::endl;); CAmount nAmount = 0; if (strAmount.empty()) { @@ -45,7 +45,7 @@ CAmount ParsePaymentAmount(const std::string& strAmount) ostr << "ParsePaymentAmount: Amount is empty"; throw std::runtime_error(ostr.str()); } - if(strAmount.size() > 20) { + if (strAmount.size() > 20) { // String is much too long, the functions below impose stricter // requirements std::ostringstream ostr; @@ -71,7 +71,7 @@ CAmount ParsePaymentAmount(const std::string& strAmount) } // Make sure there's no more than 1 decimal point - if ((pos != std::string::npos) && (strAmount.find(".", pos+1) != std::string::npos)) { + if ((pos != std::string::npos) && (strAmount.find(".", pos + 1) != std::string::npos)) { std::ostringstream ostr; ostr << "ParsePaymentAmount: Invalid amount string, too many decimal points"; throw std::runtime_error(ostr.str()); @@ -92,7 +92,7 @@ CAmount ParsePaymentAmount(const std::string& strAmount) throw std::runtime_error(ostr.str()); } - DBG( std::cout << "ParsePaymentAmount Returning true nAmount = " << nAmount << std::endl; ); + DBG(std::cout << "ParsePaymentAmount Returning true nAmount = " << nAmount << std::endl;); return nAmount; } @@ -103,44 +103,42 @@ CAmount ParsePaymentAmount(const std::string& strAmount) bool CGovernanceTriggerManager::AddNewTrigger(uint256 nHash) { - DBG( std::cout << "CGovernanceTriggerManager::AddNewTrigger: Start" << std::endl; ); + DBG(std::cout << "CGovernanceTriggerManager::AddNewTrigger: Start" << std::endl;); AssertLockHeld(governance.cs); // IF WE ALREADY HAVE THIS HASH, RETURN - if(mapTrigger.count(nHash)) { + if (mapTrigger.count(nHash)) { DBG( std::cout << "CGovernanceTriggerManager::AddNewTrigger: Already have hash" - << ", nHash = " << nHash.GetHex() - << ", count = " << mapTrigger.count(nHash) - << ", mapTrigger.size() = " << mapTrigger.size() - << std::endl; ); + << ", nHash = " << nHash.GetHex() + << ", count = " << mapTrigger.count(nHash) + << ", mapTrigger.size() = " << mapTrigger.size() + << std::endl;); return false; } CSuperblock_sptr pSuperblock; - try { + try { CSuperblock_sptr pSuperblockTmp(new CSuperblock(nHash)); pSuperblock = pSuperblockTmp; - } - catch(std::exception& e) { - DBG( std::cout << "CGovernanceTriggerManager::AddNewTrigger Error creating superblock" - << ", e.what() = " << e.what() - << std::endl; ); + } catch (std::exception& e) { + DBG(std::cout << "CGovernanceTriggerManager::AddNewTrigger Error creating superblock" + << ", e.what() = " << e.what() + << std::endl;); LogPrintf("CGovernanceTriggerManager::AddNewTrigger -- Error creating superblock: %s\n", e.what()); return false; - } - catch(...) { + } catch (...) { LogPrintf("CGovernanceTriggerManager::AddNewTrigger: Unknown Error creating superblock\n"); - DBG( std::cout << "CGovernanceTriggerManager::AddNewTrigger Error creating superblock catchall" << std::endl; ); + DBG(std::cout << "CGovernanceTriggerManager::AddNewTrigger Error creating superblock catchall" << std::endl;); return false; } pSuperblock->SetStatus(SEEN_OBJECT_IS_VALID); - DBG( std::cout << "CGovernanceTriggerManager::AddNewTrigger: Inserting trigger" << std::endl; ); + DBG(std::cout << "CGovernanceTriggerManager::AddNewTrigger: Inserting trigger" << std::endl;); mapTrigger.insert(std::make_pair(nHash, pSuperblock)); - DBG( std::cout << "CGovernanceTriggerManager::AddNewTrigger: End" << std::endl; ); + DBG(std::cout << "CGovernanceTriggerManager::AddNewTrigger: End" << std::endl;); return true; } @@ -154,33 +152,33 @@ bool CGovernanceTriggerManager::AddNewTrigger(uint256 nHash) void CGovernanceTriggerManager::CleanAndRemove() { LogPrint("gobject", "CGovernanceTriggerManager::CleanAndRemove -- Start\n"); - DBG( std::cout << "CGovernanceTriggerManager::CleanAndRemove: Start" << std::endl; ); + DBG(std::cout << "CGovernanceTriggerManager::CleanAndRemove: Start" << std::endl;); AssertLockHeld(governance.cs); // Remove triggers that are invalid or expired - DBG( std::cout << "CGovernanceTriggerManager::CleanAndRemove: mapTrigger.size() = " << mapTrigger.size() << std::endl; ); + DBG(std::cout << "CGovernanceTriggerManager::CleanAndRemove: mapTrigger.size() = " << mapTrigger.size() << std::endl;); LogPrint("gobject", "CGovernanceTriggerManager::CleanAndRemove -- mapTrigger.size() = %d\n", mapTrigger.size()); trigger_m_it it = mapTrigger.begin(); - while(it != mapTrigger.end()) { + while (it != mapTrigger.end()) { bool remove = false; CGovernanceObject* pObj = nullptr; CSuperblock_sptr& pSuperblock = it->second; - if(!pSuperblock) { - DBG( std::cout << "CGovernanceTriggerManager::CleanAndRemove: NULL superblock marked for removal" << std::endl; ); + if (!pSuperblock) { + DBG(std::cout << "CGovernanceTriggerManager::CleanAndRemove: NULL superblock marked for removal" << std::endl;); LogPrint("gobject", "CGovernanceTriggerManager::CleanAndRemove -- NULL superblock marked for removal\n"); remove = true; } else { pObj = governance.FindGovernanceObject(it->first); - if(!pObj || pObj->GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) { - DBG( std::cout << "CGovernanceTriggerManager::CleanAndRemove: Unknown or non-trigger superblock" << std::endl; ); + if (!pObj || pObj->GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) { + DBG(std::cout << "CGovernanceTriggerManager::CleanAndRemove: Unknown or non-trigger superblock" << std::endl;); LogPrint("gobject", "CGovernanceTriggerManager::CleanAndRemove -- Unknown or non-trigger superblock\n"); pSuperblock->SetStatus(SEEN_OBJECT_ERROR_INVALID); } - DBG( std::cout << "CGovernanceTriggerManager::CleanAndRemove: superblock status = " << pSuperblock->GetStatus() << std::endl; ); + DBG(std::cout << "CGovernanceTriggerManager::CleanAndRemove: superblock status = " << pSuperblock->GetStatus() << std::endl;); LogPrint("gobject", "CGovernanceTriggerManager::CleanAndRemove -- superblock status = %d\n", pSuperblock->GetStatus()); - switch(pSuperblock->GetStatus()) { + switch (pSuperblock->GetStatus()) { case SEEN_OBJECT_ERROR_INVALID: case SEEN_OBJECT_UNKNOWN: LogPrint("gobject", "CGovernanceTriggerManager::CleanAndRemove -- Unknown or invalid trigger found\n"); @@ -196,16 +194,15 @@ void CGovernanceTriggerManager::CleanAndRemove() } LogPrint("gobject", "CGovernanceTriggerManager::CleanAndRemove -- %smarked for removal\n", remove ? "" : "NOT "); - if(remove) { + if (remove) { DBG( std::string strDataAsPlainString = "NULL"; - if(pObj) { + if (pObj) { strDataAsPlainString = pObj->GetDataAsPlainString(); } std::cout << "CGovernanceTriggerManager::CleanAndRemove: Removing object: " - << strDataAsPlainString - << std::endl; - ); + << strDataAsPlainString + << std::endl;); LogPrint("gobject", "CGovernanceTriggerManager::CleanAndRemove -- Removing trigger object\n"); // mark corresponding object for deletion if (pObj) { @@ -216,13 +213,12 @@ void CGovernanceTriggerManager::CleanAndRemove() } // delete the trigger mapTrigger.erase(it++); - } - else { + } else { ++it; } } - DBG( std::cout << "CGovernanceTriggerManager::CleanAndRemove: End" << std::endl; ); + DBG(std::cout << "CGovernanceTriggerManager::CleanAndRemove: End" << std::endl;); } /** @@ -237,22 +233,18 @@ std::vector CGovernanceTriggerManager::GetActiveTriggers() AssertLockHeld(governance.cs); std::vector vecResults; - DBG( std::cout << "GetActiveTriggers: mapTrigger.size() = " << mapTrigger.size() << std::endl; ); + DBG(std::cout << "GetActiveTriggers: mapTrigger.size() = " << mapTrigger.size() << std::endl;); // LOOK AT THESE OBJECTS AND COMPILE A VALID LIST OF TRIGGERS - trigger_m_it it = mapTrigger.begin(); - while(it != mapTrigger.end()) { - - CGovernanceObject* pObj = governance.FindGovernanceObject((*it).first); - - if(pObj) { - DBG( std::cout << "GetActiveTriggers: pObj->GetDataAsPlainString() = " << pObj->GetDataAsPlainString() << std::endl; ); - vecResults.push_back(it->second); + for (const auto& pair : mapTrigger) { + CGovernanceObject* pObj = governance.FindGovernanceObject(pair.first); + if (pObj) { + DBG(std::cout << "GetActiveTriggers: pObj->GetDataAsPlainString() = " << pObj->GetDataAsPlainString() << std::endl;); + vecResults.push_back(pair.second); } - ++it; } - DBG( std::cout << "GetActiveTriggers: vecResults.size() = " << vecResults.size() << std::endl; ); + DBG(std::cout << "GetActiveTriggers: vecResults.size() = " << vecResults.size() << std::endl;); return vecResults; } @@ -276,21 +268,20 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight) LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- vecTriggers.size() = %d\n", vecTriggers.size()); - DBG( std::cout << "IsSuperblockTriggered Number triggers = " << vecTriggers.size() << std::endl; ); + DBG(std::cout << "IsSuperblockTriggered Number triggers = " << vecTriggers.size() << std::endl;); - for (const auto& pSuperblock : vecTriggers) - { - if(!pSuperblock) { + for (const auto& pSuperblock : vecTriggers) { + if (!pSuperblock) { LogPrintf("CSuperblockManager::IsSuperblockTriggered -- Non-superblock found, continuing\n"); - DBG( std::cout << "IsSuperblockTriggered Not a superblock, continuing " << std::endl; ); + DBG(std::cout << "IsSuperblockTriggered Not a superblock, continuing " << std::endl;); continue; } CGovernanceObject* pObj = pSuperblock->GetGovernanceObject(); - if(!pObj) { - LogPrintf("CSuperblockManager::IsSuperblockTriggered -- pObj == NULL, continuing\n"); - DBG( std::cout << "IsSuperblockTriggered pObj is NULL, continuing" << std::endl; ); + if (!pObj) { + LogPrintf("CSuperblockManager::IsSuperblockTriggered -- pObj == nullptr, continuing\n"); + DBG(std::cout << "IsSuperblockTriggered pObj is NULL, continuing" << std::endl;); continue; } @@ -298,14 +289,14 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight) // note : 12.1 - is epoch calculation correct? - if(nBlockHeight != pSuperblock->GetBlockHeight()) { + if (nBlockHeight != pSuperblock->GetBlockHeight()) { LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- block height doesn't match nBlockHeight = %d, blockStart = %d, continuing\n", - nBlockHeight, - pSuperblock->GetBlockHeight()); - DBG( std::cout << "IsSuperblockTriggered Not the target block, continuing" - << ", nBlockHeight = " << nBlockHeight - << ", superblock->GetBlockHeight() = " << pSuperblock->GetBlockHeight() - << std::endl; ); + nBlockHeight, + pSuperblock->GetBlockHeight()); + DBG(std::cout << "IsSuperblockTriggered Not the target block, continuing" + << ", nBlockHeight = " << nBlockHeight + << ", superblock->GetBlockHeight() = " << pSuperblock->GetBlockHeight() + << std::endl;); continue; } @@ -313,14 +304,13 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight) pObj->UpdateSentinelVariables(); - if(pObj->IsSetCachedFunding()) { + if (pObj->IsSetCachedFunding()) { LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- fCacheFunding = true, returning true\n"); - DBG( std::cout << "IsSuperblockTriggered returning true" << std::endl; ); + DBG(std::cout << "IsSuperblockTriggered returning true" << std::endl;); return true; - } - else { + } else { LogPrint("gobject", "CSuperblockManager::IsSuperblockTriggered -- fCacheFunding = false, continuing\n"); - DBG( std::cout << "IsSuperblockTriggered No fCachedFunding, continuing" << std::endl; ); + DBG(std::cout << "IsSuperblockTriggered No fCachedFunding, continuing" << std::endl;); } } @@ -330,7 +320,7 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight) bool CSuperblockManager::GetBestSuperblock(CSuperblock_sptr& pSuperblockRet, int nBlockHeight) { - if(!CSuperblock::IsValidBlockHeight(nBlockHeight)) { + if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) { return false; } @@ -339,31 +329,31 @@ bool CSuperblockManager::GetBestSuperblock(CSuperblock_sptr& pSuperblockRet, int int nYesCount = 0; for (const auto& pSuperblock : vecTriggers) { - if(!pSuperblock) { - DBG( std::cout << "GetBestSuperblock Not a superblock, continuing" << std::endl; ); + if (!pSuperblock) { + DBG(std::cout << "GetBestSuperblock Not a superblock, continuing" << std::endl;); continue; } CGovernanceObject* pObj = pSuperblock->GetGovernanceObject(); - if(!pObj) { - DBG( std::cout << "GetBestSuperblock pObj is NULL, continuing" << std::endl; ); + if (!pObj) { + DBG(std::cout << "GetBestSuperblock pObj is NULL, continuing" << std::endl;); continue; } - if(nBlockHeight != pSuperblock->GetBlockHeight()) { - DBG( std::cout << "GetBestSuperblock Not the target block, continuing" << std::endl; ); + if (nBlockHeight != pSuperblock->GetBlockHeight()) { + DBG(std::cout << "GetBestSuperblock Not the target block, continuing" << std::endl;); continue; } // DO WE HAVE A NEW WINNER? int nTempYesCount = pObj->GetAbsoluteYesCount(VOTE_SIGNAL_FUNDING); - DBG( std::cout << "GetBestSuperblock nTempYesCount = " << nTempYesCount << std::endl; ); - if(nTempYesCount > nYesCount) { + DBG(std::cout << "GetBestSuperblock nTempYesCount = " << nTempYesCount << std::endl;); + if (nTempYesCount > nYesCount) { nYesCount = nTempYesCount; pSuperblockRet = pSuperblock; - DBG( std::cout << "GetBestSuperblock Valid superblock found, pSuperblock set" << std::endl; ); + DBG(std::cout << "GetBestSuperblock Valid superblock found, pSuperblock set" << std::endl;); } } @@ -371,47 +361,46 @@ bool CSuperblockManager::GetBestSuperblock(CSuperblock_sptr& pSuperblockRet, int } /** -* Create Superblock Payments +* Get Superblock Payments * -* - Create the correct payment structure for a given superblock +* - Returns payments for superblock */ -void CSuperblockManager::CreateSuperblock(CMutableTransaction& txNewRet, int nBlockHeight, std::vector& voutSuperblockRet) +bool CSuperblockManager::GetSuperblockPayments(int nBlockHeight, std::vector& voutSuperblockRet) { - DBG( std::cout << "CSuperblockManager::CreateSuperblock Start" << std::endl; ); + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments Start" << std::endl;); LOCK(governance.cs); // GET THE BEST SUPERBLOCK FOR THIS BLOCK HEIGHT CSuperblock_sptr pSuperblock; - if(!CSuperblockManager::GetBestSuperblock(pSuperblock, nBlockHeight)) { - LogPrint("gobject", "CSuperblockManager::CreateSuperblock -- Can't find superblock for height %d\n", nBlockHeight); - DBG( std::cout << "CSuperblockManager::CreateSuperblock Failed to get superblock for height, returning" << std::endl; ); - return; + if (!CSuperblockManager::GetBestSuperblock(pSuperblock, nBlockHeight)) { + LogPrint("gobject", "CSuperblockManager::GetSuperblockPayments -- Can't find superblock for height %d\n", nBlockHeight); + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments Failed to get superblock for height, returning" << std::endl;); + return false; } // make sure it's empty, just in case voutSuperblockRet.clear(); - // CONFIGURE SUPERBLOCK OUTPUTS + // GET SUPERBLOCK OUTPUTS - // Superblock payments are appended to the end of the coinbase vout vector - DBG( std::cout << "CSuperblockManager::CreateSuperblock Number payments: " << pSuperblock->CountPayments() << std::endl; ); + // Superblock payments will be appended to the end of the coinbase vout vector + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments Number payments: " << pSuperblock->CountPayments() << std::endl;); // TODO: How many payments can we add before things blow up? // Consider at least following limits: // - max coinbase tx size // - max "budget" available - for(int i = 0; i < pSuperblock->CountPayments(); i++) { + for (int i = 0; i < pSuperblock->CountPayments(); i++) { CGovernancePayment payment; - DBG( std::cout << "CSuperblockManager::CreateSuperblock i = " << i << std::endl; ); - if(pSuperblock->GetPayment(i, payment)) { - DBG( std::cout << "CSuperblockManager::CreateSuperblock Payment found " << std::endl; ); + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments i = " << i << std::endl;); + if (pSuperblock->GetPayment(i, payment)) { + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments Payment found " << std::endl;); // SET COINBASE OUTPUT TO SUPERBLOCK SETTING CTxOut txout = CTxOut(payment.nAmount, payment.script); - txNewRet.vout.push_back(txout); voutSuperblockRet.push_back(txout); // PRINT NICE LOG OUTPUT FOR SUPERBLOCK PAYMENT @@ -422,15 +411,17 @@ void CSuperblockManager::CreateSuperblock(CMutableTransaction& txNewRet, int nBl // TODO: PRINT NICE N.N ABSOLUTE OUTPUT - DBG( std::cout << "CSuperblockManager::CreateSuperblock Before LogPrintf call, nAmount = " << payment.nAmount << std::endl; ); + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments Before LogPrintf call, nAmount = " << payment.nAmount << std::endl;); LogPrintf("NEW Superblock : output %d (addr %s, amount %d)\n", i, address2.ToString(), payment.nAmount); - DBG( std::cout << "CSuperblockManager::CreateSuperblock After LogPrintf call " << std::endl; ); + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments After LogPrintf call " << std::endl;); } else { - DBG( std::cout << "CSuperblockManager::CreateSuperblock Payment not found " << std::endl; ); + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments Payment not found " << std::endl;); } } - DBG( std::cout << "CSuperblockManager::CreateSuperblock End" << std::endl; ); + DBG(std::cout << "CSuperblockManager::GetSuperblockPayments End" << std::endl;); + + return true; } bool CSuperblockManager::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward) @@ -439,7 +430,7 @@ bool CSuperblockManager::IsValid(const CTransaction& txNew, int nBlockHeight, CA LOCK(governance.cs); CSuperblock_sptr pSuperblock; - if(CSuperblockManager::GetBestSuperblock(pSuperblock, nBlockHeight)) { + if (CSuperblockManager::GetBestSuperblock(pSuperblock, nBlockHeight)) { return pSuperblock->IsValid(txNew, nBlockHeight, blockReward); } @@ -451,7 +442,7 @@ void CSuperblockManager::ExecuteBestSuperblock(int nBlockHeight) LOCK(governance.cs); CSuperblock_sptr pSuperblock; - if(GetBestSuperblock(pSuperblock, nBlockHeight)) { + if (GetBestSuperblock(pSuperblock, nBlockHeight)) { // All checks are done in CSuperblock::IsValid via IsBlockValueValid and IsBlockPayeeValid, // tip wouldn't be updated if anything was wrong. Mark this trigger as executed. pSuperblock->SetExecuted(); @@ -459,36 +450,37 @@ void CSuperblockManager::ExecuteBestSuperblock(int nBlockHeight) } CSuperblock:: -CSuperblock() - : nGovObjHash(), - nBlockHeight(0), - nStatus(SEEN_OBJECT_UNKNOWN), - vecPayments() -{} + CSuperblock() : + nGovObjHash(), + nBlockHeight(0), + nStatus(SEEN_OBJECT_UNKNOWN), + vecPayments() +{ +} CSuperblock:: -CSuperblock(uint256& nHash) - : nGovObjHash(nHash), - nBlockHeight(0), - nStatus(SEEN_OBJECT_UNKNOWN), - vecPayments() + CSuperblock(uint256& nHash) : + nGovObjHash(nHash), + nBlockHeight(0), + nStatus(SEEN_OBJECT_UNKNOWN), + vecPayments() { - DBG( std::cout << "CSuperblock Constructor Start" << std::endl; ); + DBG(std::cout << "CSuperblock Constructor Start" << std::endl;); CGovernanceObject* pGovObj = GetGovernanceObject(); - if(!pGovObj) { - DBG( std::cout << "CSuperblock Constructor pGovObjIn is NULL, returning" << std::endl; ); + if (!pGovObj) { + DBG(std::cout << "CSuperblock Constructor pGovObjIn is NULL, returning" << std::endl;); throw std::runtime_error("CSuperblock: Failed to find Governance Object"); } - DBG( std::cout << "CSuperblock Constructor pGovObj : " - << pGovObj->GetDataAsPlainString() - << ", nObjectType = " << pGovObj->GetObjectType() - << std::endl; ); + DBG(std::cout << "CSuperblock Constructor pGovObj : " + << pGovObj->GetDataAsPlainString() + << ", nObjectType = " << pGovObj->GetObjectType() + << std::endl;); if (pGovObj->GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) { - DBG( std::cout << "CSuperblock Constructor pGovObj not a trigger, returning" << std::endl; ); + DBG(std::cout << "CSuperblock Constructor pGovObj not a trigger, returning" << std::endl;); throw std::runtime_error("CSuperblock: Governance Object not a trigger"); } @@ -503,9 +495,9 @@ CSuperblock(uint256& nHash) ParsePaymentSchedule(strAddresses, strAmounts); LogPrint("gobject", "CSuperblock -- nBlockHeight = %d, strAddresses = %s, strAmounts = %s, vecPayments.size() = %d\n", - nBlockHeight, strAddresses, strAmounts, vecPayments.size()); + nBlockHeight, strAddresses, strAmounts, vecPayments.size()); - DBG( std::cout << "CSuperblock Constructor End" << std::endl; ); + DBG(std::cout << "CSuperblock Constructor End" << std::endl;); } /** @@ -518,7 +510,7 @@ bool CSuperblock::IsValidBlockHeight(int nBlockHeight) { // SUPERBLOCKS CAN HAPPEN ONLY after hardfork and only ONCE PER CYCLE return nBlockHeight >= Params().GetConsensus().nSuperblockStartBlock && - ((nBlockHeight % Params().GetConsensus().nSuperblockCycle) == 0); + ((nBlockHeight % Params().GetConsensus().nSuperblockCycle) == 0); } void CSuperblock::GetNearestSuperblocksHeights(int nBlockHeight, int& nLastSuperblockRet, int& nNextSuperblockRet) @@ -531,7 +523,7 @@ void CSuperblock::GetNearestSuperblocksHeights(int nBlockHeight, int& nLastSuper int nFirstSuperblockOffset = (nSuperblockCycle - nSuperblockStartBlock % nSuperblockCycle) % nSuperblockCycle; int nFirstSuperblock = nSuperblockStartBlock + nFirstSuperblockOffset; - if(nBlockHeight < nFirstSuperblock) { + if (nBlockHeight < nFirstSuperblock) { nLastSuperblockRet = 0; nNextSuperblockRet = nFirstSuperblock; } else { @@ -544,7 +536,7 @@ CAmount CSuperblock::GetPaymentsLimit(int nBlockHeight) { const Consensus::Params& consensusParams = Params().GetConsensus(); - if(!IsValidBlockHeight(nBlockHeight)) { + if (!IsValidBlockHeight(nBlockHeight)) { return 0; } @@ -589,13 +581,13 @@ void CSuperblock::ParsePaymentSchedule(const std::string& strPaymentAddresses, c AMOUNTS = [AMOUNT1|2|3|4|5|6] */ - DBG( std::cout << "CSuperblock::ParsePaymentSchedule vecParsed1.size() = " << vecParsed1.size() << std::endl; ); + DBG(std::cout << "CSuperblock::ParsePaymentSchedule vecParsed1.size() = " << vecParsed1.size() << std::endl;); for (int i = 0; i < (int)vecParsed1.size(); i++) { CBitcoinAddress address(vecParsed1[i]); if (!address.IsValid()) { std::ostringstream ostr; - ostr << "CSuperblock::ParsePaymentSchedule -- Invalid Absolute Address : " << vecParsed1[i]; + ostr << "CSuperblock::ParsePaymentSchedule -- Invalid Absolute Address : " << vecParsed1[i]; LogPrintf("%s\n", ostr.str()); throw std::runtime_error(ostr.str()); } @@ -605,29 +597,28 @@ void CSuperblock::ParsePaymentSchedule(const std::string& strPaymentAddresses, c - There might be an issue with multisig in the coinbase on mainnet, we will add support for it in a future release. - Post 12.3+ (test multisig coinbase transaction) */ - if(address.IsScript()) { + if (address.IsScript()) { std::ostringstream ostr; - ostr << "CSuperblock::ParsePaymentSchedule -- Script addresses are not supported yet : " << vecParsed1[i]; + ostr << "CSuperblock::ParsePaymentSchedule -- Script addresses are not supported yet : " << vecParsed1[i]; LogPrintf("%s\n", ostr.str()); throw std::runtime_error(ostr.str()); } - DBG( std::cout << "CSuperblock::ParsePaymentSchedule i = " << i - << ", vecParsed2[i] = " << vecParsed2[i] - << std::endl; ); + DBG(std::cout << "CSuperblock::ParsePaymentSchedule i = " << i + << ", vecParsed2[i] = " << vecParsed2[i] + << std::endl;); CAmount nAmount = ParsePaymentAmount(vecParsed2[i]); - DBG( std::cout << "CSuperblock::ParsePaymentSchedule: " - << "amount string = " << vecParsed2[i] - << ", nAmount = " << nAmount - << std::endl; ); + DBG(std::cout << "CSuperblock::ParsePaymentSchedule: " + << "amount string = " << vecParsed2[i] + << ", nAmount = " << nAmount + << std::endl;); CGovernancePayment payment(address, nAmount); - if(payment.IsValid()) { + if (payment.IsValid()) { vecPayments.push_back(payment); - } - else { + } else { vecPayments.clear(); std::ostringstream ostr; ostr << "CSuperblock::ParsePaymentSchedule -- Invalid payment found: address = " << address.ToString() @@ -640,7 +631,7 @@ void CSuperblock::ParsePaymentSchedule(const std::string& strPaymentAddresses, c bool CSuperblock::GetPayment(int nPaymentIndex, CGovernancePayment& paymentRet) { - if((nPaymentIndex<0) || (nPaymentIndex >= (int)vecPayments.size())) { + if ((nPaymentIndex < 0) || (nPaymentIndex >= (int)vecPayments.size())) { return false; } @@ -653,7 +644,7 @@ CAmount CSuperblock::GetPaymentsTotalAmount() CAmount nPaymentsTotalAmount = 0; int nPayments = CountPayments(); - for(int i = 0; i < nPayments; i++) { + for (int i = 0; i < nPayments; i++) { nPaymentsTotalAmount += vecPayments[i].nAmount; } @@ -673,7 +664,7 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b // internal to *this and since CSuperblock's are accessed only through // shared pointers there's no way our object can get deleted while this // code is running. - if(!IsValidBlockHeight(nBlockHeight)) { + if (!IsValidBlockHeight(nBlockHeight)) { LogPrintf("CSuperblock::IsValid -- ERROR: Block invalid, incorrect block height\n"); return false; } @@ -684,15 +675,15 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b int nOutputs = txNew.vout.size(); int nPayments = CountPayments(); - int nMinerPayments = nOutputs - nPayments; + int nMinerAndMasternodePayments = nOutputs - nPayments; LogPrint("gobject", "CSuperblock::IsValid nOutputs = %d, nPayments = %d, GetDataAsHexString = %s\n", - nOutputs, nPayments, GetGovernanceObject()->GetDataAsHexString()); + nOutputs, nPayments, GetGovernanceObject()->GetDataAsHexString()); // We require an exact match (including order) between the expected // superblock payments and the payments actually in the block. - if(nMinerPayments < 0) { + if (nMinerAndMasternodePayments < 0) { // This means the block cannot have all the superblock payments // so it is not valid. // TODO: could that be that we just hit coinbase size limit? @@ -703,22 +694,22 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b // payments should not exceed limit CAmount nPaymentsTotalAmount = GetPaymentsTotalAmount(); CAmount nPaymentsLimit = GetPaymentsLimit(nBlockHeight); - if(nPaymentsTotalAmount > nPaymentsLimit) { + if (nPaymentsTotalAmount > nPaymentsLimit) { LogPrintf("CSuperblock::IsValid -- ERROR: Block invalid, payments limit exceeded: payments %lld, limit %lld\n", nPaymentsTotalAmount, nPaymentsLimit); return false; } - // miner should not get more than he would usually get + // miner and masternodes should not get more than they would usually get CAmount nBlockValue = txNew.GetValueOut(); - if(nBlockValue > blockReward + nPaymentsTotalAmount) { + if (nBlockValue > blockReward + nPaymentsTotalAmount) { LogPrintf("CSuperblock::IsValid -- ERROR: Block invalid, block value limit exceeded: block %lld, limit %lld\n", nBlockValue, blockReward + nPaymentsTotalAmount); return false; } int nVoutIndex = 0; - for(int i = 0; i < nPayments; i++) { + for (int i = 0; i < nPayments; i++) { CGovernancePayment payment; - if(!GetPayment(i, payment)) { + if (!GetPayment(i, payment)) { // This shouldn't happen so log a warning LogPrintf("CSuperblock::IsValid -- WARNING: Failed to find payment: %d of %d total payments\n", i, nPayments); continue; @@ -737,7 +728,7 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b } } - if(!fPaymentMatch) { + if (!fPaymentMatch) { // Superblock payment not found! CTxDestination address1; @@ -759,26 +750,26 @@ bool CSuperblock::IsExpired() // Executed triggers are kept for another superblock cycle (approximately 1 month), // other valid triggers are kept for ~1 day only, everything else is pruned after ~1h. switch (nStatus) { - case SEEN_OBJECT_EXECUTED: - nExpirationBlocks = Params().GetConsensus().nSuperblockCycle; - break; - case SEEN_OBJECT_IS_VALID: - nExpirationBlocks = 576; - break; - default: - nExpirationBlocks = 24; - break; + case SEEN_OBJECT_EXECUTED: + nExpirationBlocks = Params().GetConsensus().nSuperblockCycle; + break; + case SEEN_OBJECT_IS_VALID: + nExpirationBlocks = 576; + break; + default: + nExpirationBlocks = 24; + break; } int nExpirationBlock = nBlockHeight + nExpirationBlocks; LogPrint("gobject", "CSuperblock::IsExpired -- nBlockHeight = %d, nExpirationBlock = %d\n", nBlockHeight, nExpirationBlock); - if(governance.GetCachedBlockHeight() > nExpirationBlock) { + if (governance.GetCachedBlockHeight() > nExpirationBlock) { LogPrint("gobject", "CSuperblock::IsExpired -- Outdated trigger found\n"); fExpired = true; CGovernanceObject* pgovobj = GetGovernanceObject(); - if(pgovobj) { + if (pgovobj) { LogPrint("gobject", "CSuperblock::IsExpired -- Expiring outdated object: %s\n", pgovobj->GetHash().ToString()); pgovobj->fExpired = true; pgovobj->nDeletionTime = GetAdjustedTime(); @@ -802,16 +793,16 @@ std::string CSuperblockManager::GetRequiredPaymentsString(int nBlockHeight) // GET BEST SUPERBLOCK CSuperblock_sptr pSuperblock; - if(!GetBestSuperblock(pSuperblock, nBlockHeight)) { + if (!GetBestSuperblock(pSuperblock, nBlockHeight)) { LogPrint("gobject", "CSuperblockManager::GetRequiredPaymentsString -- Can't find superblock for height %d\n", nBlockHeight); return "error"; } // LOOP THROUGH SUPERBLOCK PAYMENTS, CONFIGURE OUTPUT STRING - for(int i = 0; i < pSuperblock->CountPayments(); i++) { + for (int i = 0; i < pSuperblock->CountPayments(); i++) { CGovernancePayment payment; - if(pSuperblock->GetPayment(i, payment)) { + if (pSuperblock->GetPayment(i, payment)) { // PRINT NICE LOG OUTPUT FOR SUPERBLOCK PAYMENT CTxDestination address1; @@ -820,10 +811,9 @@ std::string CSuperblockManager::GetRequiredPaymentsString(int nBlockHeight) // RETURN NICE OUTPUT FOR CONSOLE - if(ret != "Unknown") { + if (ret != "Unknown") { ret += ", " + address2.ToString(); - } - else { + } else { ret = address2.ToString(); } } diff --git a/src/governance-classes.h b/src/governance-classes.h index d15ffc9abe20..730ad0967ba4 100644 --- a/src/governance-classes.h +++ b/src/governance-classes.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef GOVERNANCE_CLASSES_H @@ -13,17 +13,14 @@ #include "script/standard.h" #include "util.h" -#include - class CSuperblock; -class CGovernanceTrigger; class CGovernanceTriggerManager; class CSuperblockManager; static const int TRIGGER_UNKNOWN = -1; -static const int TRIGGER_SUPERBLOCK = 1000; +static const int TRIGGER_SUPERBLOCK = 2500; -typedef boost::shared_ptr CSuperblock_sptr; +typedef std::shared_ptr CSuperblock_sptr; // DECLARE GLOBAL VARIABLES FOR GOVERNANCE CLASSES extern CGovernanceTriggerManager triggerman; @@ -43,7 +40,6 @@ class CGovernanceTriggerManager private: typedef std::map trigger_m_t; typedef trigger_m_t::iterator trigger_m_it; - typedef trigger_m_t::const_iterator trigger_m_cit; trigger_m_t mapTrigger; @@ -52,7 +48,8 @@ class CGovernanceTriggerManager void CleanAndRemove(); public: - CGovernanceTriggerManager() : mapTrigger() {} + CGovernanceTriggerManager() : + mapTrigger() {} }; /** @@ -67,10 +64,9 @@ class CSuperblockManager static bool GetBestSuperblock(CSuperblock_sptr& pSuperblockRet, int nBlockHeight); public: - static bool IsSuperblockTriggered(int nBlockHeight); - static void CreateSuperblock(CMutableTransaction& txNewRet, int nBlockHeight, std::vector& voutSuperblockRet); + static bool GetSuperblockPayments(int nBlockHeight, std::vector& voutSuperblockRet); static void ExecuteBestSuperblock(int nBlockHeight); static std::string GetRequiredPaymentsString(int nBlockHeight); @@ -91,33 +87,29 @@ class CGovernancePayment CScript script; CAmount nAmount; - CGovernancePayment() - :fValid(false), - script(), - nAmount(0) - {} + CGovernancePayment() : + fValid(false), + script(), + nAmount(0) + { + } - CGovernancePayment(CBitcoinAddress addrIn, CAmount nAmountIn) - :fValid(false), - script(), - nAmount(0) + CGovernancePayment(CBitcoinAddress addrIn, CAmount nAmountIn) : + fValid(false), + script(), + nAmount(0) { - try - { + try { CTxDestination dest = addrIn.Get(); script = GetScriptForDestination(dest); nAmount = nAmountIn; fValid = true; - } - catch(std::exception& e) - { + } catch (std::exception& e) { LogPrintf("CGovernancePayment Payment not valid: addrIn = %s, nAmountIn = %d, what = %s\n", - addrIn.ToString(), nAmountIn, e.what()); - } - catch(...) - { + addrIn.ToString(), nAmountIn, e.what()); + } catch (...) { LogPrintf("CGovernancePayment Payment not valid: addrIn = %s, nAmountIn = %d\n", - addrIn.ToString(), nAmountIn); + addrIn.ToString(), nAmountIn); } } @@ -154,7 +146,6 @@ class CSuperblock : public CGovernanceObject void ParsePaymentSchedule(const std::string& strPaymentAddresses, const std::string& strPaymentAmounts); public: - CSuperblock(); CSuperblock(uint256& nHash); diff --git a/src/governance-exceptions.h b/src/governance-exceptions.h index b8b9c2638d06..2f1aacec07d9 100644 --- a/src/governance-exceptions.h +++ b/src/governance-exceptions.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -26,7 +26,7 @@ enum governance_exception_type_enum_t { inline std::ostream& operator<<(std::ostream& os, governance_exception_type_enum_t eType) { - switch(eType) { + switch (eType) { case GOVERNANCE_EXCEPTION_NONE: os << "GOVERNANCE_EXCEPTION_NONE"; break; @@ -64,11 +64,11 @@ class CGovernanceException : public std::exception public: CGovernanceException(const std::string& strMessageIn = "", - governance_exception_type_enum_t eTypeIn = GOVERNANCE_EXCEPTION_NONE, - int nNodePenaltyIn = 0) - : strMessage(), - eType(eTypeIn), - nNodePenalty(nNodePenaltyIn) + governance_exception_type_enum_t eTypeIn = GOVERNANCE_EXCEPTION_NONE, + int nNodePenaltyIn = 0) : + strMessage(), + eType(eTypeIn), + nNodePenalty(nNodePenaltyIn) { std::ostringstream ostr; ostr << eType << ":" << strMessageIn; @@ -77,7 +77,7 @@ class CGovernanceException : public std::exception virtual ~CGovernanceException() throw() {} - virtual const char* what() const throw() override + virtual const char* what() const throw() override { return strMessage.c_str(); } @@ -92,7 +92,8 @@ class CGovernanceException : public std::exception return eType; } - int GetNodePenalty() const { + int GetNodePenalty() const + { return nNodePenalty; } }; diff --git a/src/governance-object.cpp b/src/governance-object.cpp index f1f5eca8e2de..8e823b7baca4 100644 --- a/src/governance-object.cpp +++ b/src/governance-object.cpp @@ -1,23 +1,25 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "governance-object.h" #include "core_io.h" -#include "governance.h" #include "governance-classes.h" -#include "governance-object.h" -#include "governance-vote.h" #include "governance-validators.h" +#include "governance-vote.h" +#include "governance.h" #include "instantx.h" #include "masternode-sync.h" #include "masternodeman.h" #include "messagesigner.h" #include "util.h" +#include #include +#include -CGovernanceObject::CGovernanceObject(): +CGovernanceObject::CGovernanceObject() : cs(), nObjectType(GOVERNANCE_OBJECT_UNKNOWN), nHashParent(), @@ -45,7 +47,7 @@ CGovernanceObject::CGovernanceObject(): LoadData(); } -CGovernanceObject::CGovernanceObject(const uint256& nHashParentIn, int nRevisionIn, int64_t nTimeIn, const uint256& nCollateralHashIn, const std::string& strDataHexIn): +CGovernanceObject::CGovernanceObject(const uint256& nHashParentIn, int nRevisionIn, int64_t nTimeIn, const uint256& nCollateralHashIn, const std::string& strDataHexIn) : cs(), nObjectType(GOVERNANCE_OBJECT_UNKNOWN), nHashParent(nHashParentIn), @@ -73,7 +75,7 @@ CGovernanceObject::CGovernanceObject(const uint256& nHashParentIn, int nRevision LoadData(); } -CGovernanceObject::CGovernanceObject(const CGovernanceObject& other): +CGovernanceObject::CGovernanceObject(const CGovernanceObject& other) : cs(), nObjectType(other.nObjectType), nHashParent(other.nHashParent), @@ -96,12 +98,13 @@ CGovernanceObject::CGovernanceObject(const CGovernanceObject& other): mapCurrentMNVotes(other.mapCurrentMNVotes), cmmapOrphanVotes(other.cmmapOrphanVotes), fileVotes(other.fileVotes) -{} +{ +} bool CGovernanceObject::ProcessVote(CNode* pfrom, - const CGovernanceVote& vote, - CGovernanceException& exception, - CConnman& connman) + const CGovernanceVote& vote, + CGovernanceException& exception, + CConnman& connman) { LOCK(cs); @@ -115,17 +118,16 @@ bool CGovernanceObject::ProcessVote(CNode* pfrom, return false; } - if(!mnodeman.Has(vote.GetMasternodeOutpoint())) { + if (!mnodeman.Has(vote.GetMasternodeOutpoint())) { std::ostringstream ostr; ostr << "CGovernanceObject::ProcessVote -- Masternode " << vote.GetMasternodeOutpoint().ToStringShort() << " not found"; exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING); - if(cmmapOrphanVotes.Insert(vote.GetMasternodeOutpoint(), vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME))) { - if(pfrom) { + if (cmmapOrphanVotes.Insert(vote.GetMasternodeOutpoint(), vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME))) { + if (pfrom) { mnodeman.AskForMN(pfrom, vote.GetMasternodeOutpoint(), connman); } LogPrintf("%s\n", ostr.str()); - } - else { + } else { LogPrint("gobject", "%s\n", ostr.str()); } return false; @@ -134,14 +136,14 @@ bool CGovernanceObject::ProcessVote(CNode* pfrom, vote_m_it it = mapCurrentMNVotes.emplace(vote_m_t::value_type(vote.GetMasternodeOutpoint(), vote_rec_t())).first; vote_rec_t& voteRecordRef = it->second; vote_signal_enum_t eSignal = vote.GetSignal(); - if(eSignal == VOTE_SIGNAL_NONE) { + if (eSignal == VOTE_SIGNAL_NONE) { std::ostringstream ostr; ostr << "CGovernanceObject::ProcessVote -- Vote signal: none"; LogPrint("gobject", "%s\n", ostr.str()); exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING); return false; } - if(eSignal > MAX_SUPPORTED_VOTE_SIGNAL) { + if (eSignal > MAX_SUPPORTED_VOTE_SIGNAL) { std::ostringstream ostr; ostr << "CGovernanceObject::ProcessVote -- Unsupported vote signal: " << CGovernanceVoting::ConvertSignalToString(vote.GetSignal()); LogPrintf("%s\n", ostr.str()); @@ -152,7 +154,7 @@ bool CGovernanceObject::ProcessVote(CNode* pfrom, vote_instance_t& voteInstanceRef = it2->second; // Reject obsolete votes - if(vote.GetTimestamp() < voteInstanceRef.nCreationTime) { + if (vote.GetTimestamp() < voteInstanceRef.nCreationTime) { std::ostringstream ostr; ostr << "CGovernanceObject::ProcessVote -- Obsolete vote"; LogPrint("gobject", "%s\n", ostr.str()); @@ -162,9 +164,9 @@ bool CGovernanceObject::ProcessVote(CNode* pfrom, int64_t nNow = GetAdjustedTime(); int64_t nVoteTimeUpdate = voteInstanceRef.nTime; - if(governance.AreRateChecksEnabled()) { + if (governance.AreRateChecksEnabled()) { int64_t nTimeDelta = nNow - voteInstanceRef.nTime; - if(nTimeDelta < GOVERNANCE_UPDATE_MIN) { + if (nTimeDelta < GOVERNANCE_UPDATE_MIN) { std::ostringstream ostr; ostr << "CGovernanceObject::ProcessVote -- Masternode voting too often" << ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort() @@ -177,20 +179,22 @@ bool CGovernanceObject::ProcessVote(CNode* pfrom, } } + bool onlyOwnerAllowed = nObjectType == GOVERNANCE_OBJECT_PROPOSAL; + // Finally check that the vote is actually valid (done last because of cost of signature verification) - if(!vote.IsValid(true)) { + if (!vote.IsValid(onlyOwnerAllowed)) { std::ostringstream ostr; ostr << "CGovernanceObject::ProcessVote -- Invalid vote" - << ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort() - << ", governance object hash = " << GetHash().ToString() - << ", vote hash = " << vote.GetHash().ToString(); + << ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort() + << ", governance object hash = " << GetHash().ToString() + << ", vote hash = " << vote.GetHash().ToString(); LogPrintf("%s\n", ostr.str()); exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20); governance.AddInvalidVote(vote); return false; } - if(!mnodeman.AddGovernanceVote(vote.GetMasternodeOutpoint(), vote.GetParentHash())) { + if (!mnodeman.AddGovernanceVote(vote.GetMasternodeOutpoint(), vote.GetParentHash())) { std::ostringstream ostr; ostr << "CGovernanceObject::ProcessVote -- Unable to add governance vote" << ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort() @@ -211,12 +215,11 @@ void CGovernanceObject::ClearMasternodeVotes() LOCK(cs); vote_m_it it = mapCurrentMNVotes.begin(); - while(it != mapCurrentMNVotes.end()) { - if(!mnodeman.Has(it->first)) { + while (it != mapCurrentMNVotes.end()) { + if (!mnodeman.Has(it->first)) { fileVotes.RemoveVotesFromMasternode(it->first); mapCurrentMNVotes.erase(it++); - } - else { + } else { ++it; } } @@ -226,11 +229,11 @@ std::string CGovernanceObject::GetSignatureMessage() const { LOCK(cs); std::string strMessage = nHashParent.ToString() + "|" + - boost::lexical_cast(nRevision) + "|" + - boost::lexical_cast(nTime) + "|" + - GetDataAsHexString() + "|" + - masternodeOutpoint.ToStringShort() + "|" + - nCollateralHash.ToString(); + std::to_string(nRevision) + "|" + + std::to_string(nTime) + "|" + + GetDataAsHexString() + "|" + + masternodeOutpoint.ToStringShort() + "|" + + nCollateralHash.ToString(); return strMessage; } @@ -250,7 +253,7 @@ uint256 CGovernanceObject::GetHash() const ss << vchSig; // fee_tx is left out on purpose - DBG( printf("CGovernanceObject::GetHash %i %li %s\n", nRevision, nTime, GetDataAsHexString().c_str()); ); + DBG(printf("CGovernanceObject::GetHash %i %li %s\n", nRevision, nTime, GetDataAsHexString().c_str());); return ss.GetHash(); } @@ -265,53 +268,53 @@ void CGovernanceObject::SetMasternodeOutpoint(const COutPoint& outpoint) masternodeOutpoint = outpoint; } -bool CGovernanceObject::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMasternode) +bool CGovernanceObject::Sign(const CKey& key, const CKeyID& keyID) { std::string strError; if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::SignHash(hash, keyMasternode, vchSig)) { + if (!CHashSigner::SignHash(hash, key, vchSig)) { LogPrintf("CGovernanceObject::Sign -- SignHash() failed\n"); return false; } - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyID, vchSig, strError)) { LogPrintf("CGovernanceObject::Sign -- VerifyHash() failed, error: %s\n", strError); return false; } } else { std::string strMessage = GetSignatureMessage(); - if (!CMessageSigner::SignMessage(strMessage, vchSig, keyMasternode)) { + if (!CMessageSigner::SignMessage(strMessage, vchSig, key)) { LogPrintf("CGovernanceObject::Sign -- SignMessage() failed\n"); return false; } - if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) { LogPrintf("CGovernanceObject::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } } LogPrint("gobject", "CGovernanceObject::Sign -- pubkey id = %s, masternode = %s\n", - pubKeyMasternode.GetID().ToString(), masternodeOutpoint.ToStringShort()); + keyID.ToString(), masternodeOutpoint.ToStringShort()); return true; } -bool CGovernanceObject::CheckSignature(const CPubKey& pubKeyMasternode) const +bool CGovernanceObject::CheckSignature(const CKeyID& keyID) const { std::string strError; if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyID, vchSig, strError)) { // could be an old object std::string strMessage = GetSignatureMessage(); - if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) { // nope, not in old format either LogPrintf("CGovernance::CheckSignature -- VerifyMessage() failed, error: %s\n", strError); return false; @@ -320,7 +323,7 @@ bool CGovernanceObject::CheckSignature(const CPubKey& pubKeyMasternode) const } else { std::string strMessage = GetSignatureMessage(); - if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) { LogPrintf("CGovernance::CheckSignature -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -329,6 +332,24 @@ bool CGovernanceObject::CheckSignature(const CPubKey& pubKeyMasternode) const return true; } +bool CGovernanceObject::Sign(const CBLSSecretKey& key) +{ + CBLSSignature sig = key.Sign(GetSignatureHash()); + sig.GetBuf(vchSig); + return true; +} + +bool CGovernanceObject::CheckSignature(const CBLSPublicKey& pubKey) const +{ + CBLSSignature sig; + sig.SetBuf(vchSig); + if (!sig.VerifyInsecure(pubKey, GetSignatureHash())) { + LogPrintf("CGovernanceObject::CheckSignature -- VerifyInsecure() failed\n"); + return false; + } + return true; +} + /** Return the actual object from the vchData JSON structure. @@ -337,7 +358,7 @@ bool CGovernanceObject::CheckSignature(const CPubKey& pubKeyMasternode) const UniValue CGovernanceObject::GetJSONObject() { UniValue obj(UniValue::VOBJ); - if(vchData.empty()) { + if (vchData.empty()) { return obj; } @@ -348,8 +369,8 @@ UniValue CGovernanceObject::GetJSONObject() obj = objResult; } else { std::vector arr1 = objResult.getValues(); - std::vector arr2 = arr1.at( 0 ).getValues(); - obj = arr2.at( 1 ); + std::vector arr2 = arr1.at(0).getValues(); + obj = arr2.at(1); } return obj; @@ -368,36 +389,34 @@ void CGovernanceObject::LoadData() // todo : 12.1 - resolved //return; - if(vchData.empty()) { + if (vchData.empty()) { return; } - try { + try { // ATTEMPT TO LOAD JSON STRING FROM VCHDATA UniValue objResult(UniValue::VOBJ); GetData(objResult); - DBG( std::cout << "CGovernanceObject::LoadData GetDataAsPlainString = " - << GetDataAsPlainString() - << std::endl; ); + DBG(std::cout << "CGovernanceObject::LoadData GetDataAsPlainString = " + << GetDataAsPlainString() + << std::endl;); UniValue obj = GetJSONObject(); nObjectType = obj["type"].get_int(); - } - catch(std::exception& e) { + } catch (std::exception& e) { fUnparsable = true; std::ostringstream ostr; ostr << "CGovernanceObject::LoadData Error parsing JSON" << ", e.what() = " << e.what(); - DBG( std::cout << ostr.str() << std::endl; ); + DBG(std::cout << ostr.str() << std::endl;); LogPrintf("%s\n", ostr.str()); return; - } - catch(...) { + } catch (...) { fUnparsable = true; std::ostringstream ostr; ostr << "CGovernanceObject::LoadData Unknown Error parsing JSON"; - DBG( std::cout << ostr.str() << std::endl; ); + DBG(std::cout << ostr.str() << std::endl;); LogPrintf("%s\n", ostr.str()); return; } @@ -461,75 +480,87 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingMast return false; } - switch(nObjectType) { - case GOVERNANCE_OBJECT_WATCHDOG: { - // watchdogs are deprecated + switch (nObjectType) { + case GOVERNANCE_OBJECT_WATCHDOG: { + // watchdogs are deprecated + return false; + } + case GOVERNANCE_OBJECT_PROPOSAL: { + CProposalValidator validator(GetDataAsHexString()); + // Note: It's ok to have expired proposals + // they are going to be cleared by CGovernanceManager::UpdateCachesAndClean() + // TODO: should they be tagged as "expired" to skip vote downloading? + if (!validator.Validate(false)) { + strError = strprintf("Invalid proposal data, error messages: %s", validator.GetErrorMessages()); return false; } - case GOVERNANCE_OBJECT_PROPOSAL: { - CProposalValidator validator(GetDataAsHexString()); - // Note: It's ok to have expired proposals - // they are going to be cleared by CGovernanceManager::UpdateCachesAndClean() - // TODO: should they be tagged as "expired" to skip vote downloading? - if (!validator.Validate(false)) { - strError = strprintf("Invalid proposal data, error messages: %s", validator.GetErrorMessages()); - return false; - } - if (fCheckCollateral && !IsCollateralValid(strError, fMissingConfirmations)) { - strError = "Invalid proposal collateral"; - return false; - } + if (fCheckCollateral && !IsCollateralValid(strError, fMissingConfirmations)) { + strError = "Invalid proposal collateral"; + return false; + } + return true; + } + case GOVERNANCE_OBJECT_TRIGGER: { + if (!fCheckCollateral) { + // nothing else we can check here (yet?) return true; } - case GOVERNANCE_OBJECT_TRIGGER: { - if (!fCheckCollateral) - // nothing else we can check here (yet?) - return true; - - std::string strOutpoint = masternodeOutpoint.ToStringShort(); - masternode_info_t infoMn; - if (!mnodeman.GetMasternodeInfo(masternodeOutpoint, infoMn)) { - - CMasternode::CollateralStatus err = CMasternode::CheckCollateral(masternodeOutpoint, CPubKey()); - if (err == CMasternode::COLLATERAL_UTXO_NOT_FOUND) { - strError = "Failed to find Masternode UTXO, missing masternode=" + strOutpoint + "\n"; - } else if (err == CMasternode::COLLATERAL_INVALID_AMOUNT) { - strError = "Masternode UTXO should have 2500 ABS, missing masternode=" + strOutpoint + "\n"; - } else if (err == CMasternode::COLLATERAL_INVALID_PUBKEY) { - fMissingMasternode = true; - strError = "Masternode not found: " + strOutpoint; - } else if (err == CMasternode::COLLATERAL_OK) { - // this should never happen with CPubKey() as a param - strError = "CheckCollateral critical failure! Masternode: " + strOutpoint; - } - return false; + std::string strOutpoint = masternodeOutpoint.ToStringShort(); + masternode_info_t infoMn; + if (!mnodeman.GetMasternodeInfo(masternodeOutpoint, infoMn)) { + CMasternode::CollateralStatus err = CMasternode::CheckCollateral(masternodeOutpoint, CKeyID()); + if (err == CMasternode::COLLATERAL_UTXO_NOT_FOUND) { + strError = "Failed to find Masternode UTXO, missing masternode=" + strOutpoint + "\n"; + } else if (err == CMasternode::COLLATERAL_UTXO_NOT_PROTX) { + strError = "Masternode UTXO is not a ProTx, missing masternode=" + strOutpoint + "\n"; + } else if (err == CMasternode::COLLATERAL_INVALID_AMOUNT) { + strError = "Masternode UTXO should have 1000 DASH, missing masternode=" + strOutpoint + "\n"; + } else if (err == CMasternode::COLLATERAL_INVALID_PUBKEY) { + fMissingMasternode = true; + strError = "Masternode not found: " + strOutpoint; + } else if (err == CMasternode::COLLATERAL_OK) { + // this should never happen with CPubKey() as a param + strError = "CheckCollateral critical failure! Masternode: " + strOutpoint; } - // Check that we have a valid MN signature - if (!CheckSignature(infoMn.pubKeyMasternode)) { - strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey id = " + infoMn.pubKeyMasternode.GetID().ToString(); + return false; + } + + // Check that we have a valid MN signature + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + if (!CheckSignature(infoMn.blsPubKeyOperator)) { + strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey id = " + infoMn.blsPubKeyOperator.ToString(); + return false; + } + } else { + if (!CheckSignature(infoMn.legacyKeyIDOperator)) { + strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey id = " + infoMn.legacyKeyIDOperator.ToString(); return false; } - - return true; - } - default: { - strError = strprintf("Invalid object type %d", nObjectType); - return false; } - } + return true; + } + default: { + strError = strprintf("Invalid object type %d", nObjectType); + return false; + } + } } CAmount CGovernanceObject::GetMinCollateralFee() const { // Only 1 type has a fee for the moment but switch statement allows for future object types - switch(nObjectType) { - case GOVERNANCE_OBJECT_PROPOSAL: return GOVERNANCE_PROPOSAL_FEE_TX; - case GOVERNANCE_OBJECT_TRIGGER: return 0; - case GOVERNANCE_OBJECT_WATCHDOG: return 0; - default: return MAX_MONEY; + switch (nObjectType) { + case GOVERNANCE_OBJECT_PROPOSAL: + return GOVERNANCE_PROPOSAL_FEE_TX; + case GOVERNANCE_OBJECT_TRIGGER: + return 0; + case GOVERNANCE_OBJECT_WATCHDOG: + return 0; + default: + return MAX_MONEY; } } @@ -545,19 +576,19 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC // RETRIEVE TRANSACTION IN QUESTION - if(!GetTransaction(nCollateralHash, txCollateral, Params().GetConsensus(), nBlockHash, true)){ + if (!GetTransaction(nCollateralHash, txCollateral, Params().GetConsensus(), nBlockHash, true)) { strError = strprintf("Can't find collateral tx %s", nCollateralHash.ToString()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } - if(nBlockHash == uint256()) { + if (nBlockHash == uint256()) { strError = strprintf("Collateral tx %s is not mined yet", txCollateral->ToString()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } - if(txCollateral->vout.size() < 1) { + if (txCollateral->vout.size() < 1) { strError = strprintf("tx vout size less than 1 | %d", txCollateral->vout.size()); LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; @@ -568,44 +599,42 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC CScript findScript; findScript << OP_RETURN << ToByteVector(nExpectedHash); - DBG( std::cout << "IsCollateralValid: txCollateral->vout.size() = " << txCollateral->vout.size() << std::endl; ); + DBG(std::cout << "IsCollateralValid: txCollateral->vout.size() = " << txCollateral->vout.size() << std::endl;); - DBG( std::cout << "IsCollateralValid: findScript = " << ScriptToAsmStr( findScript, false ) << std::endl; ); + DBG(std::cout << "IsCollateralValid: findScript = " << ScriptToAsmStr(findScript, false) << std::endl;); - DBG( std::cout << "IsCollateralValid: nMinFee = " << nMinFee << std::endl; ); + DBG(std::cout << "IsCollateralValid: nMinFee = " << nMinFee << std::endl;); bool foundOpReturn = false; for (const auto& output : txCollateral->vout) { - DBG( std::cout << "IsCollateralValid txout : " << output.ToString() - << ", output.nValue = " << output.nValue - << ", output.scriptPubKey = " << ScriptToAsmStr( output.scriptPubKey, false ) - << std::endl; ); - if(!output.scriptPubKey.IsPayToPublicKeyHash() && !output.scriptPubKey.IsUnspendable()) { + DBG(std::cout << "IsCollateralValid txout : " << output.ToString() + << ", output.nValue = " << output.nValue + << ", output.scriptPubKey = " << ScriptToAsmStr(output.scriptPubKey, false) + << std::endl;); + if (!output.scriptPubKey.IsPayToPublicKeyHash() && !output.scriptPubKey.IsUnspendable()) { strError = strprintf("Invalid Script %s", txCollateral->ToString()); - LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError); + LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } - if(output.scriptPubKey == findScript && output.nValue >= nMinFee) { - DBG( std::cout << "IsCollateralValid foundOpReturn = true" << std::endl; ); + if (output.scriptPubKey == findScript && output.nValue >= nMinFee) { + DBG(std::cout << "IsCollateralValid foundOpReturn = true" << std::endl;); foundOpReturn = true; + } else { + DBG(std::cout << "IsCollateralValid No match, continuing" << std::endl;); } - else { - DBG( std::cout << "IsCollateralValid No match, continuing" << std::endl; ); - } - } - if(!foundOpReturn){ + if (!foundOpReturn) { strError = strprintf("Couldn't find opReturn %s in %s", nExpectedHash.ToString(), txCollateral->ToString()); - LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError); + LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } // GET CONFIRMATIONS FOR TRANSACTION AssertLockHeld(cs_main); - int nConfirmationsIn = instantsend.GetConfirmations(nCollateralHash); + int nConfirmationsIn = 0; if (nBlockHash != uint256()) { BlockMap::iterator mi = mapBlockIndex.find(nBlockHash); if (mi != mapBlockIndex.end() && (*mi).second) { @@ -616,7 +645,8 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC } } - if(nConfirmationsIn < GOVERNANCE_FEE_CONFIRMATIONS) { + if ((nConfirmationsIn < GOVERNANCE_FEE_CONFIRMATIONS) && + (!instantsend.IsLockedInstantSendTransaction(nCollateralHash))) { strError = strprintf("Collateral requires at least %d confirmations to be relayed throughout the network (it has only %d)", GOVERNANCE_FEE_CONFIRMATIONS, nConfirmationsIn); if (nConfirmationsIn >= GOVERNANCE_MIN_RELAY_FEE_CONFIRMATIONS) { fMissingConfirmations = true; @@ -624,7 +654,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC } else { strError += ", rejected -- try again later"; } - LogPrintf ("CGovernanceObject::IsCollateralValid -- %s\n", strError); + LogPrintf("CGovernanceObject::IsCollateralValid -- %s\n", strError); return false; } @@ -641,7 +671,7 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote for (const auto& votepair : mapCurrentMNVotes) { const vote_rec_t& recVote = votepair.second; vote_instance_m_cit it2 = recVote.mapInstances.find(eVoteSignalIn); - if(it2 != recVote.mapInstances.end() && it2->second.eOutcome == eVoteOutcomeIn) { + if (it2 != recVote.mapInstances.end() && it2->second.eOutcome == eVoteOutcomeIn) { ++nCount; } } @@ -686,13 +716,13 @@ bool CGovernanceObject::GetCurrentMNVotes(const COutPoint& mnCollateralOutpoint, return false; } voteRecord = it->second; - return true; + return true; } void CGovernanceObject::Relay(CConnman& connman) { // Do not relay until fully synced - if(!masternodeSync.IsSynced()) { + if (!masternodeSync.IsSynced()) { LogPrint("gobject", "CGovernanceObject::Relay -- won't relay until fully synced\n"); return; } @@ -706,7 +736,7 @@ void CGovernanceObject::UpdateSentinelVariables() // CALCULATE MINIMUM SUPPORT LEVELS REQUIRED int nMnCount = mnodeman.CountEnabled(); - if(nMnCount == 0) return; + if (nMnCount == 0) return; // CALCULATE THE MINUMUM VOTE COUNT REQUIRED FOR FULL SIGNAL @@ -723,40 +753,16 @@ void CGovernanceObject::UpdateSentinelVariables() // SET SENTINEL FLAGS TO TRUE IF MIMIMUM SUPPORT LEVELS ARE REACHED // ARE ANY OF THESE FLAGS CURRENTLY ACTIVATED? - if(GetAbsoluteYesCount(VOTE_SIGNAL_FUNDING) >= nAbsVoteReq) fCachedFunding = true; - if((GetAbsoluteYesCount(VOTE_SIGNAL_DELETE) >= nAbsDeleteReq) && !fCachedDelete) { + if (GetAbsoluteYesCount(VOTE_SIGNAL_FUNDING) >= nAbsVoteReq) fCachedFunding = true; + if ((GetAbsoluteYesCount(VOTE_SIGNAL_DELETE) >= nAbsDeleteReq) && !fCachedDelete) { fCachedDelete = true; - if(nDeletionTime == 0) { + if (nDeletionTime == 0) { nDeletionTime = GetAdjustedTime(); } } - if(GetAbsoluteYesCount(VOTE_SIGNAL_ENDORSED) >= nAbsVoteReq) fCachedEndorsed = true; + if (GetAbsoluteYesCount(VOTE_SIGNAL_ENDORSED) >= nAbsVoteReq) fCachedEndorsed = true; - if(GetAbsoluteNoCount(VOTE_SIGNAL_VALID) >= nAbsVoteReq) fCachedValid = false; -} - -void CGovernanceObject::swap(CGovernanceObject& first, CGovernanceObject& second) // nothrow -{ - // enable ADL (not necessary in our case, but good practice) - using std::swap; - - // by swapping the members of two classes, - // the two classes are effectively swapped - swap(first.nHashParent, second.nHashParent); - swap(first.nRevision, second.nRevision); - swap(first.nTime, second.nTime); - swap(first.nDeletionTime, second.nDeletionTime); - swap(first.nCollateralHash, second.nCollateralHash); - swap(first.vchData, second.vchData); - swap(first.nObjectType, second.nObjectType); - - // swap all cached valid flags - swap(first.fCachedFunding, second.fCachedFunding); - swap(first.fCachedValid, second.fCachedValid); - swap(first.fCachedDelete, second.fCachedDelete); - swap(first.fCachedEndorsed, second.fCachedEndorsed); - swap(first.fDirtyCache, second.fDirtyCache); - swap(first.fExpired, second.fExpired); + if (GetAbsoluteNoCount(VOTE_SIGNAL_VALID) >= nAbsVoteReq) fCachedValid = false; } void CGovernanceObject::CheckOrphanVotes(CConnman& connman) @@ -764,28 +770,26 @@ void CGovernanceObject::CheckOrphanVotes(CConnman& connman) int64_t nNow = GetAdjustedTime(); const vote_cmm_t::list_t& listVotes = cmmapOrphanVotes.GetItemList(); vote_cmm_t::list_cit it = listVotes.begin(); - while(it != listVotes.end()) { + while (it != listVotes.end()) { bool fRemove = false; const COutPoint& key = it->key; const vote_time_pair_t& pairVote = it->value; const CGovernanceVote& vote = pairVote.first; - if(pairVote.second < nNow) { + if (pairVote.second < nNow) { fRemove = true; - } - else if(!mnodeman.Has(vote.GetMasternodeOutpoint())) { + } else if (!mnodeman.Has(vote.GetMasternodeOutpoint())) { ++it; continue; } CGovernanceException exception; - if(!ProcessVote(NULL, vote, exception, connman)) { + if (!ProcessVote(nullptr, vote, exception, connman)) { LogPrintf("CGovernanceObject::CheckOrphanVotes -- Failed to add orphan vote: %s\n", exception.what()); - } - else { + } else { vote.Relay(connman); fRemove = true; } ++it; - if(fRemove) { + if (fRemove) { cmmapOrphanVotes.Erase(key, pairVote); } } diff --git a/src/governance-object.h b/src/governance-object.h index e4a5551b7b1e..d3b2385cd7ec 100644 --- a/src/governance-object.h +++ b/src/governance-object.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -17,6 +17,7 @@ #include "sync.h" #include "util.h" #include "utilstrencodings.h" +#include "bls/bls.h" #include @@ -25,8 +26,7 @@ class CGovernanceTriggerManager; class CGovernanceObject; class CGovernanceVote; -static const int MAX_GOVERNANCE_OBJECT_DATA_SIZE = 16 * 1024; -static const int MIN_GOVERNANCE_PEER_PROTO_VERSION = 70208; +static const int MIN_GOVERNANCE_PEER_PROTO_VERSION = 70210; static const int GOVERNANCE_FILTER_PROTO_VERSION = 70206; static const double GOVERNANCE_FILTER_FP_RATE = 0.001; @@ -36,20 +36,20 @@ static const int GOVERNANCE_OBJECT_PROPOSAL = 1; static const int GOVERNANCE_OBJECT_TRIGGER = 2; static const int GOVERNANCE_OBJECT_WATCHDOG = 3; -static const CAmount GOVERNANCE_PROPOSAL_FEE_TX = (5.0*COIN); +static const CAmount GOVERNANCE_PROPOSAL_FEE_TX = (5.0 * COIN); static const int64_t GOVERNANCE_FEE_CONFIRMATIONS = 6; static const int64_t GOVERNANCE_MIN_RELAY_FEE_CONFIRMATIONS = 1; -static const int64_t GOVERNANCE_UPDATE_MIN = 60*60; -static const int64_t GOVERNANCE_DELETION_DELAY = 10*60; -static const int64_t GOVERNANCE_ORPHAN_EXPIRATION_TIME = 10*60; +static const int64_t GOVERNANCE_UPDATE_MIN = 60 * 60; +static const int64_t GOVERNANCE_DELETION_DELAY = 10 * 60; +static const int64_t GOVERNANCE_ORPHAN_EXPIRATION_TIME = 10 * 60; // FOR SEEN MAP ARRAYS - GOVERNANCE OBJECTS AND VOTES static const int SEEN_OBJECT_IS_VALID = 0; static const int SEEN_OBJECT_ERROR_INVALID = 1; static const int SEEN_OBJECT_ERROR_IMMATURE = 2; static const int SEEN_OBJECT_EXECUTED = 3; //used for triggers -static const int SEEN_OBJECT_UNKNOWN = 4; // the default +static const int SEEN_OBJECT_UNKNOWN = 4; // the default typedef std::pair vote_time_pair_t; @@ -59,16 +59,16 @@ inline bool operator<(const vote_time_pair_t& p1, const vote_time_pair_t& p2) } struct vote_instance_t { - vote_outcome_enum_t eOutcome; int64_t nTime; int64_t nCreationTime; - vote_instance_t(vote_outcome_enum_t eOutcomeIn = VOTE_OUTCOME_NONE, int64_t nTimeIn = 0, int64_t nCreationTimeIn = 0) - : eOutcome(eOutcomeIn), - nTime(nTimeIn), - nCreationTime(nCreationTimeIn) - {} + vote_instance_t(vote_outcome_enum_t eOutcomeIn = VOTE_OUTCOME_NONE, int64_t nTimeIn = 0, int64_t nCreationTimeIn = 0) : + eOutcome(eOutcomeIn), + nTime(nTimeIn), + nCreationTime(nCreationTimeIn) + { + } ADD_SERIALIZE_METHODS; @@ -79,13 +79,13 @@ struct vote_instance_t { READWRITE(nOutcome); READWRITE(nTime); READWRITE(nCreationTime); - if(ser_action.ForRead()) { + if (ser_action.ForRead()) { eOutcome = vote_outcome_enum_t(nOutcome); } } }; -typedef std::map vote_instance_m_t; +typedef std::map vote_instance_m_t; typedef vote_instance_m_t::iterator vote_instance_m_it; @@ -96,11 +96,11 @@ struct vote_rec_t { ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action) - { - READWRITE(mapInstances); - } + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(mapInstances); + } }; /** @@ -195,67 +195,75 @@ class CGovernanceObject CGovernanceObject(const CGovernanceObject& other); - void swap(CGovernanceObject& first, CGovernanceObject& second); // nothrow - // Public Getter methods - int64_t GetCreationTime() const { + int64_t GetCreationTime() const + { return nTime; } - int64_t GetDeletionTime() const { + int64_t GetDeletionTime() const + { return nDeletionTime; } - int GetObjectType() const { + int GetObjectType() const + { return nObjectType; } - const uint256& GetCollateralHash() const { + const uint256& GetCollateralHash() const + { return nCollateralHash; } - const COutPoint& GetMasternodeOutpoint() const { + const COutPoint& GetMasternodeOutpoint() const + { return masternodeOutpoint; } - bool IsSetCachedFunding() const { + bool IsSetCachedFunding() const + { return fCachedFunding; } - bool IsSetCachedValid() const { + bool IsSetCachedValid() const + { return fCachedValid; } - bool IsSetCachedDelete() const { + bool IsSetCachedDelete() const + { return fCachedDelete; } - bool IsSetCachedEndorsed() const { + bool IsSetCachedEndorsed() const + { return fCachedEndorsed; } - bool IsSetDirtyCache() const { + bool IsSetDirtyCache() const + { return fDirtyCache; } - bool IsSetExpired() const { + bool IsSetExpired() const + { return fExpired; } - void InvalidateVoteCache() { - fDirtyCache = true; - } - - const CGovernanceObjectVoteFile& GetVoteFile() const { + const CGovernanceObjectVoteFile& GetVoteFile() const + { return fileVotes; } // Signature related functions void SetMasternodeOutpoint(const COutPoint& outpoint); - bool Sign(const CKey& keyMasternode, const CPubKey& pubKeyMasternode); - bool CheckSignature(const CPubKey& pubKeyMasternode) const; + bool Sign(const CKey& key, const CKeyID& keyID); + bool CheckSignature(const CKeyID& keyID) const; + bool Sign(const CBLSSecretKey& key); + bool CheckSignature(const CBLSPublicKey& pubKey) const; std::string GetSignatureMessage() const; uint256 GetSignatureHash() const; @@ -306,44 +314,17 @@ class CGovernanceObject inline void SerializationOp(Stream& s, Operation ser_action) { // SERIALIZE DATA FOR SAVING/LOADING OR NETWORK FUNCTIONS - int nVersion = s.GetVersion(); READWRITE(nHashParent); READWRITE(nRevision); READWRITE(nTime); READWRITE(nCollateralHash); - if (nVersion == 70209 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - std::string strDataHex; - if (ser_action.ForRead()) { - READWRITE(LIMITED_STRING(strDataHex, MAX_GOVERNANCE_OBJECT_DATA_SIZE)); - vchData = ParseHex(strDataHex); - } else { - strDataHex = HexStr(vchData); - READWRITE(LIMITED_STRING(strDataHex, MAX_GOVERNANCE_OBJECT_DATA_SIZE)); - } - } else { - // using new format directly - READWRITE(vchData); - } + READWRITE(vchData); READWRITE(nObjectType); - if (nVersion == 70209 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin; - if (ser_action.ForRead()) { - READWRITE(txin); - masternodeOutpoint = txin.prevout; - } else { - txin = CTxIn(masternodeOutpoint); - READWRITE(txin); - } - } else { - // using new format directly - READWRITE(masternodeOutpoint); - } + READWRITE(masternodeOutpoint); if (!(s.GetType() & SER_GETHASH)) { READWRITE(vchSig); } - if(s.GetType() & SER_DISK) { + if (s.GetType() & SER_DISK) { // Only include these for the disk file format LogPrint("gobject", "CGovernanceObject::SerializationOp Reading/writing votes from/to disk\n"); READWRITE(nDeletionTime); @@ -356,27 +337,20 @@ class CGovernanceObject // AFTER DESERIALIZATION OCCURS, CACHED VARIABLES MUST BE CALCULATED MANUALLY } - CGovernanceObject& operator=(CGovernanceObject from) - { - swap(*this, from); - return *this; - } - private: // FUNCTIONS FOR DEALING WITH DATA STRING void LoadData(); void GetData(UniValue& objResult); bool ProcessVote(CNode* pfrom, - const CGovernanceVote& vote, - CGovernanceException& exception, - CConnman& connman); + const CGovernanceVote& vote, + CGovernanceException& exception, + CConnman& connman); /// Called when MN's which have voted on this object have been removed void ClearMasternodeVotes(); void CheckOrphanVotes(CConnman& connman); - }; diff --git a/src/governance-validators.cpp b/src/governance-validators.cpp index b40fce61b2ec..34f5374d0d69 100644 --- a/src/governance-validators.cpp +++ b/src/governance-validators.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -12,15 +12,15 @@ #include -const size_t MAX_DATA_SIZE = 512; -const size_t MAX_NAME_SIZE = 40; +const size_t MAX_DATA_SIZE = 512; +const size_t MAX_NAME_SIZE = 40; CProposalValidator::CProposalValidator(const std::string& strHexData) : objJSON(UniValue::VOBJ), fJSONValid(false), strErrorMessages() { - if(!strHexData.empty()) { + if (!strHexData.empty()) { ParseStrHexData(strHexData); } } @@ -37,27 +37,27 @@ void CProposalValidator::ParseStrHexData(const std::string& strHexData) bool CProposalValidator::Validate(bool fCheckExpiration) { - if(!fJSONValid) { + if (!fJSONValid) { strErrorMessages += "JSON parsing error;"; return false; } - if(!ValidateName()) { + if (!ValidateName()) { strErrorMessages += "Invalid name;"; return false; } - if(!ValidateStartEndEpoch(fCheckExpiration)) { + if (!ValidateStartEndEpoch(fCheckExpiration)) { strErrorMessages += "Invalid start:end range;"; return false; } - if(!ValidatePaymentAmount()) { + if (!ValidatePaymentAmount()) { strErrorMessages += "Invalid payment amount;"; return false; } - if(!ValidatePaymentAddress()) { + if (!ValidatePaymentAddress()) { strErrorMessages += "Invalid payment address;"; return false; } - if(!ValidateURL()) { + if (!ValidateURL()) { strErrorMessages += "Invalid URL;"; return false; } @@ -67,12 +67,12 @@ bool CProposalValidator::Validate(bool fCheckExpiration) bool CProposalValidator::ValidateName() { std::string strName; - if(!GetDataValue("name", strName)) { + if (!GetDataValue("name", strName)) { strErrorMessages += "name field not found;"; return false; } - if(strName.size() > MAX_NAME_SIZE) { + if (strName.size() > MAX_NAME_SIZE) { strErrorMessages += strprintf("name exceeds %lu characters;", MAX_NAME_SIZE); return false; } @@ -81,7 +81,7 @@ bool CProposalValidator::ValidateName() std::transform(strName.begin(), strName.end(), strName.begin(), ::tolower); - if(strName.find_first_not_of(strAllowedChars) != std::string::npos) { + if (strName.find_first_not_of(strAllowedChars) != std::string::npos) { strErrorMessages += "name contains invalid characters;"; return false; } @@ -94,22 +94,22 @@ bool CProposalValidator::ValidateStartEndEpoch(bool fCheckExpiration) int64_t nStartEpoch = 0; int64_t nEndEpoch = 0; - if(!GetDataValue("start_epoch", nStartEpoch)) { + if (!GetDataValue("start_epoch", nStartEpoch)) { strErrorMessages += "start_epoch field not found;"; return false; } - if(!GetDataValue("end_epoch", nEndEpoch)) { + if (!GetDataValue("end_epoch", nEndEpoch)) { strErrorMessages += "end_epoch field not found;"; return false; } - if(nEndEpoch <= nStartEpoch) { + if (nEndEpoch <= nStartEpoch) { strErrorMessages += "end_epoch <= start_epoch;"; return false; } - if(fCheckExpiration && nEndEpoch <= GetAdjustedTime()) { + if (fCheckExpiration && nEndEpoch <= GetAdjustedTime()) { strErrorMessages += "expired;"; return false; } @@ -121,12 +121,12 @@ bool CProposalValidator::ValidatePaymentAmount() { double dValue = 0.0; - if(!GetDataValue("payment_amount", dValue)) { + if (!GetDataValue("payment_amount", dValue)) { strErrorMessages += "payment_amount field not found;"; return false; } - if(dValue <= 0.0) { + if (dValue <= 0.0) { strErrorMessages += "payment_amount is negative;"; return false; } @@ -142,23 +142,23 @@ bool CProposalValidator::ValidatePaymentAddress() { std::string strPaymentAddress; - if(!GetDataValue("payment_address", strPaymentAddress)) { + if (!GetDataValue("payment_address", strPaymentAddress)) { strErrorMessages += "payment_address field not found;"; return false; } - if(std::find_if(strPaymentAddress.begin(), strPaymentAddress.end(), ::isspace) != strPaymentAddress.end()) { + if (std::find_if(strPaymentAddress.begin(), strPaymentAddress.end(), ::isspace) != strPaymentAddress.end()) { strErrorMessages += "payment_address can't have whitespaces;"; return false; } CBitcoinAddress address(strPaymentAddress); - if(!address.IsValid()) { + if (!address.IsValid()) { strErrorMessages += "payment_address is invalid;"; return false; } - if(address.IsScript()) { + if (address.IsScript()) { strErrorMessages += "script addresses are not supported;"; return false; } @@ -169,22 +169,22 @@ bool CProposalValidator::ValidatePaymentAddress() bool CProposalValidator::ValidateURL() { std::string strURL; - if(!GetDataValue("url", strURL)) { + if (!GetDataValue("url", strURL)) { strErrorMessages += "url field not found;"; return false; } - if(std::find_if(strURL.begin(), strURL.end(), ::isspace) != strURL.end()) { + if (std::find_if(strURL.begin(), strURL.end(), ::isspace) != strURL.end()) { strErrorMessages += "url can't have whitespaces;"; return false; } - if(strURL.size() < 4U) { + if (strURL.size() < 4U) { strErrorMessages += "url too short;"; return false; } - if(!CheckURL(strURL)) { + if (!CheckURL(strURL)) { strErrorMessages += "url invalid;"; return false; } @@ -196,7 +196,7 @@ void CProposalValidator::ParseJSONData(const std::string& strJSONData) { fJSONValid = false; - if(strJSONData.empty()) { + if (strJSONData.empty()) { return; } @@ -214,11 +214,9 @@ void CProposalValidator::ParseJSONData(const std::string& strJSONData) } fJSONValid = true; - } - catch(std::exception& e) { + } catch (std::exception& e) { strErrorMessages += std::string(e.what()) + std::string(";"); - } - catch(...) { + } catch (...) { strErrorMessages += "Unknown exception;"; } } @@ -226,14 +224,12 @@ void CProposalValidator::ParseJSONData(const std::string& strJSONData) bool CProposalValidator::GetDataValue(const std::string& strKey, std::string& strValueRet) { bool fOK = false; - try { + try { strValueRet = objJSON[strKey].get_str(); fOK = true; - } - catch(std::exception& e) { + } catch (std::exception& e) { strErrorMessages += std::string(e.what()) + std::string(";"); - } - catch(...) { + } catch (...) { strErrorMessages += "Unknown exception;"; } return fOK; @@ -242,9 +238,9 @@ bool CProposalValidator::GetDataValue(const std::string& strKey, std::string& st bool CProposalValidator::GetDataValue(const std::string& strKey, int64_t& nValueRet) { bool fOK = false; - try { + try { const UniValue uValue = objJSON[strKey]; - switch(uValue.getType()) { + switch (uValue.getType()) { case UniValue::VNUM: nValueRet = uValue.get_int64(); fOK = true; @@ -252,11 +248,9 @@ bool CProposalValidator::GetDataValue(const std::string& strKey, int64_t& nValue default: break; } - } - catch(std::exception& e) { + } catch (std::exception& e) { strErrorMessages += std::string(e.what()) + std::string(";"); - } - catch(...) { + } catch (...) { strErrorMessages += "Unknown exception;"; } return fOK; @@ -265,9 +259,9 @@ bool CProposalValidator::GetDataValue(const std::string& strKey, int64_t& nValue bool CProposalValidator::GetDataValue(const std::string& strKey, double& dValueRet) { bool fOK = false; - try { + try { const UniValue uValue = objJSON[strKey]; - switch(uValue.getType()) { + switch (uValue.getType()) { case UniValue::VNUM: dValueRet = uValue.get_real(); fOK = true; @@ -275,11 +269,9 @@ bool CProposalValidator::GetDataValue(const std::string& strKey, double& dValueR default: break; } - } - catch(std::exception& e) { + } catch (std::exception& e) { strErrorMessages += std::string(e.what()) + std::string(";"); - } - catch(...) { + } catch (...) { strErrorMessages += "Unknown exception;"; } return fOK; @@ -296,32 +288,31 @@ bool CProposalValidator::CheckURL(const std::string& strURLIn) std::string strRest(strURLIn); std::string::size_type nPos = strRest.find(':'); - if(nPos != std::string::npos) { + if (nPos != std::string::npos) { //std::string strSchema = strRest.substr(0,nPos); - if(nPos < strRest.size()) { + if (nPos < strRest.size()) { strRest = strRest.substr(nPos + 1); - } - else { + } else { strRest = ""; } } // Process netloc - if((strRest.size() > 2) && (strRest.substr(0,2) == "//")) { + if ((strRest.size() > 2) && (strRest.substr(0, 2) == "//")) { static const std::string strNetlocDelimiters = "/?#"; strRest = strRest.substr(2); std::string::size_type nPos2 = strRest.find_first_of(strNetlocDelimiters); - std::string strNetloc = strRest.substr(0,nPos2); + std::string strNetloc = strRest.substr(0, nPos2); - if((strNetloc.find('[') != std::string::npos) && (strNetloc.find(']') == std::string::npos)) { + if ((strNetloc.find('[') != std::string::npos) && (strNetloc.find(']') == std::string::npos)) { return false; } - if((strNetloc.find(']') != std::string::npos) && (strNetloc.find('[') == std::string::npos)) { + if ((strNetloc.find(']') != std::string::npos) && (strNetloc.find('[') == std::string::npos)) { return false; } } diff --git a/src/governance-validators.h b/src/governance-validators.h index 61a206fb5b74..00d88d627df2 100644 --- a/src/governance-validators.h +++ b/src/governance-validators.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,9 +13,9 @@ class CProposalValidator { private: - UniValue objJSON; - bool fJSONValid; - std::string strErrorMessages; + UniValue objJSON; + bool fJSONValid; + std::string strErrorMessages; public: CProposalValidator(const std::string& strDataHexIn = std::string()); diff --git a/src/governance-vote.cpp b/src/governance-vote.cpp index 2707c0341b64..06dac34b9c7f 100644 --- a/src/governance-vote.cpp +++ b/src/governance-vote.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -10,73 +10,63 @@ #include "messagesigner.h" #include "util.h" -#include - std::string CGovernanceVoting::ConvertOutcomeToString(vote_outcome_enum_t nOutcome) { - switch(nOutcome) - { - case VOTE_OUTCOME_NONE: - return "NONE"; break; - case VOTE_OUTCOME_YES: - return "YES"; break; - case VOTE_OUTCOME_NO: - return "NO"; break; - case VOTE_OUTCOME_ABSTAIN: - return "ABSTAIN"; break; + static const std::map mapOutcomeString = { + { VOTE_OUTCOME_NONE, "none" }, + { VOTE_OUTCOME_YES, "yes" }, + { VOTE_OUTCOME_NO, "no" }, + { VOTE_OUTCOME_ABSTAIN, "abstain" } }; + + const auto& it = mapOutcomeString.find(nOutcome); + if (it == mapOutcomeString.end()) { + LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown outcome %d\n", __func__, nOutcome); + return "error"; } - return "error"; + return it->second; } std::string CGovernanceVoting::ConvertSignalToString(vote_signal_enum_t nSignal) { - std::string strReturn = "NONE"; - switch(nSignal) - { - case VOTE_SIGNAL_NONE: - strReturn = "NONE"; - break; - case VOTE_SIGNAL_FUNDING: - strReturn = "FUNDING"; - break; - case VOTE_SIGNAL_VALID: - strReturn = "VALID"; - break; - case VOTE_SIGNAL_DELETE: - strReturn = "DELETE"; - break; - case VOTE_SIGNAL_ENDORSED: - strReturn = "ENDORSED"; - break; + static const std::map mapSignalsString = { + { VOTE_SIGNAL_FUNDING, "funding" }, + { VOTE_SIGNAL_VALID, "valid" }, + { VOTE_SIGNAL_DELETE, "delete" }, + { VOTE_SIGNAL_ENDORSED, "endorsed" } }; + + const auto& it = mapSignalsString.find(nSignal); + if (it == mapSignalsString.end()) { + LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown signal %d\n", __func__, nSignal); + return "none"; } - - return strReturn; + return it->second; } vote_outcome_enum_t CGovernanceVoting::ConvertVoteOutcome(const std::string& strVoteOutcome) { - vote_outcome_enum_t eVote = VOTE_OUTCOME_NONE; - if(strVoteOutcome == "yes") { - eVote = VOTE_OUTCOME_YES; + static const std::map mapStringOutcome = { + { "none", VOTE_OUTCOME_NONE }, + { "yes", VOTE_OUTCOME_YES }, + { "no", VOTE_OUTCOME_NO }, + { "abstain", VOTE_OUTCOME_ABSTAIN } }; + + const auto& it = mapStringOutcome.find(strVoteOutcome); + if (it == mapStringOutcome.end()) { + LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown outcome %s\n", __func__, strVoteOutcome); + return VOTE_OUTCOME_NONE; } - else if(strVoteOutcome == "no") { - eVote = VOTE_OUTCOME_NO; - } - else if(strVoteOutcome == "abstain") { - eVote = VOTE_OUTCOME_ABSTAIN; - } - return eVote; + return it->second; + } vote_signal_enum_t CGovernanceVoting::ConvertVoteSignal(const std::string& strVoteSignal) { - static const std::map mapStrVoteSignals = { - {"funding", VOTE_SIGNAL_FUNDING}, - {"valid", VOTE_SIGNAL_VALID}, - {"delete", VOTE_SIGNAL_DELETE}, - {"endorsed", VOTE_SIGNAL_ENDORSED} - }; + static const std::map mapStrVoteSignals = { + {"funding", VOTE_SIGNAL_FUNDING}, + {"valid", VOTE_SIGNAL_VALID}, + {"delete", VOTE_SIGNAL_DELETE}, + {"endorsed", VOTE_SIGNAL_ENDORSED}}; const auto& it = mapStrVoteSignals.find(strVoteSignal); if (it == mapStrVoteSignals.end()) { @@ -86,26 +76,27 @@ vote_signal_enum_t CGovernanceVoting::ConvertVoteSignal(const std::string& strVo return it->second; } -CGovernanceVote::CGovernanceVote() - : fValid(true), - fSynced(false), - nVoteSignal(int(VOTE_SIGNAL_NONE)), - masternodeOutpoint(), - nParentHash(), - nVoteOutcome(int(VOTE_OUTCOME_NONE)), - nTime(0), - vchSig() -{} - -CGovernanceVote::CGovernanceVote(const COutPoint& outpointMasternodeIn, const uint256& nParentHashIn, vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) - : fValid(true), - fSynced(false), - nVoteSignal(eVoteSignalIn), - masternodeOutpoint(outpointMasternodeIn), - nParentHash(nParentHashIn), - nVoteOutcome(eVoteOutcomeIn), - nTime(GetAdjustedTime()), - vchSig() +CGovernanceVote::CGovernanceVote() : + fValid(true), + fSynced(false), + nVoteSignal(int(VOTE_SIGNAL_NONE)), + masternodeOutpoint(), + nParentHash(), + nVoteOutcome(int(VOTE_OUTCOME_NONE)), + nTime(0), + vchSig() +{ +} + +CGovernanceVote::CGovernanceVote(const COutPoint& outpointMasternodeIn, const uint256& nParentHashIn, vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) : + fValid(true), + fSynced(false), + nVoteSignal(eVoteSignalIn), + masternodeOutpoint(outpointMasternodeIn), + nParentHash(nParentHashIn), + nVoteOutcome(eVoteOutcomeIn), + nTime(GetAdjustedTime()), + vchSig() { UpdateHash(); } @@ -123,7 +114,7 @@ std::string CGovernanceVote::ToString() const void CGovernanceVote::Relay(CConnman& connman) const { // Do not relay until fully synced - if(!masternodeSync.IsSynced()) { + if (!masternodeSync.IsSynced()) { LogPrint("gobject", "CGovernanceVote::Relay -- won't relay until fully synced\n"); return; } @@ -155,33 +146,32 @@ uint256 CGovernanceVote::GetSignatureHash() const return SerializeHash(*this); } -bool CGovernanceVote::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMasternode) +bool CGovernanceVote::Sign(const CKey& key, const CKeyID& keyID) { std::string strError; if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if(!CHashSigner::SignHash(hash, keyMasternode, vchSig)) { + if (!CHashSigner::SignHash(hash, key, vchSig)) { LogPrintf("CGovernanceVote::Sign -- SignHash() failed\n"); return false; } - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyID, vchSig, strError)) { LogPrintf("CGovernanceVote::Sign -- VerifyHash() failed, error: %s\n", strError); return false; } } else { - std::string strMessage = masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" + - boost::lexical_cast(nVoteSignal) + "|" + boost::lexical_cast(nVoteOutcome) + "|" + boost::lexical_cast(nTime); + std::to_string(nVoteSignal) + "|" + std::to_string(nVoteOutcome) + "|" + std::to_string(nTime); - if(!CMessageSigner::SignMessage(strMessage, vchSig, keyMasternode)) { + if (!CMessageSigner::SignMessage(strMessage, vchSig, key)) { LogPrintf("CGovernanceVote::Sign -- SignMessage() failed\n"); return false; } - if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) { LogPrintf("CGovernanceVote::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -190,21 +180,21 @@ bool CGovernanceVote::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMaste return true; } -bool CGovernanceVote::CheckSignature(const CPubKey& pubKeyMasternode) const +bool CGovernanceVote::CheckSignature(const CKeyID& keyID) const { std::string strError; if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyID, vchSig, strError)) { // could be a signature in old format std::string strMessage = masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" + - boost::lexical_cast(nVoteSignal) + "|" + - boost::lexical_cast(nVoteOutcome) + "|" + - boost::lexical_cast(nTime); + std::to_string(nVoteSignal) + "|" + + std::to_string(nVoteOutcome) + "|" + + std::to_string(nTime); - if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) { // nope, not in old format either LogPrint("gobject", "CGovernanceVote::IsValid -- VerifyMessage() failed, error: %s\n", strError); return false; @@ -212,11 +202,11 @@ bool CGovernanceVote::CheckSignature(const CPubKey& pubKeyMasternode) const } } else { std::string strMessage = masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" + - boost::lexical_cast(nVoteSignal) + "|" + - boost::lexical_cast(nVoteOutcome) + "|" + - boost::lexical_cast(nTime); + std::to_string(nVoteSignal) + "|" + + std::to_string(nVoteOutcome) + "|" + + std::to_string(nTime); - if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) { LogPrint("gobject", "CGovernanceVote::IsValid -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -225,36 +215,60 @@ bool CGovernanceVote::CheckSignature(const CPubKey& pubKeyMasternode) const return true; } -bool CGovernanceVote::IsValid(bool fSignatureCheck) const +bool CGovernanceVote::Sign(const CBLSSecretKey& key) +{ + uint256 hash = GetSignatureHash(); + CBLSSignature sig = key.Sign(hash); + sig.GetBuf(vchSig); + return true; +} + +bool CGovernanceVote::CheckSignature(const CBLSPublicKey& pubKey) const { - if(nTime > GetAdjustedTime() + (60*60)) { - LogPrint("gobject", "CGovernanceVote::IsValid -- vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", GetHash().ToString(), nTime, GetAdjustedTime() + (60*60)); + uint256 hash = GetSignatureHash(); + CBLSSignature sig; + sig.SetBuf(vchSig); + if (!sig.VerifyInsecure(pubKey, hash)) { + LogPrintf("CGovernanceVote::CheckSignature -- VerifyInsecure() failed\n"); + return false; + } + return true; +} + +bool CGovernanceVote::IsValid(bool useVotingKey) const +{ + if (nTime > GetAdjustedTime() + (60 * 60)) { + LogPrint("gobject", "CGovernanceVote::IsValid -- vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", GetHash().ToString(), nTime, GetAdjustedTime() + (60 * 60)); return false; } // support up to MAX_SUPPORTED_VOTE_SIGNAL, can be extended - if(nVoteSignal > MAX_SUPPORTED_VOTE_SIGNAL) - { + if (nVoteSignal > MAX_SUPPORTED_VOTE_SIGNAL) { LogPrint("gobject", "CGovernanceVote::IsValid -- Client attempted to vote on invalid signal(%d) - %s\n", nVoteSignal, GetHash().ToString()); return false; } // 0=none, 1=yes, 2=no, 3=abstain. Beyond that reject votes - if(nVoteOutcome > 3) - { + if (nVoteOutcome > 3) { LogPrint("gobject", "CGovernanceVote::IsValid -- Client attempted to vote on invalid outcome(%d) - %s\n", nVoteSignal, GetHash().ToString()); return false; } masternode_info_t infoMn; - if(!mnodeman.GetMasternodeInfo(masternodeOutpoint, infoMn)) { + if (!mnodeman.GetMasternodeInfo(masternodeOutpoint, infoMn)) { LogPrint("gobject", "CGovernanceVote::IsValid -- Unknown Masternode - %s\n", masternodeOutpoint.ToStringShort()); return false; } - if(!fSignatureCheck) return true; - - return CheckSignature(infoMn.pubKeyMasternode); + if (useVotingKey) { + return CheckSignature(infoMn.keyIDVoting); + } else { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + return CheckSignature(infoMn.blsPubKeyOperator); + } else { + return CheckSignature(infoMn.legacyKeyIDOperator); + } + } } bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2) @@ -270,25 +284,25 @@ bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2) bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2) { bool fResult = (vote1.masternodeOutpoint < vote2.masternodeOutpoint); - if(!fResult) { + if (!fResult) { return false; } fResult = (vote1.masternodeOutpoint == vote2.masternodeOutpoint); fResult = fResult && (vote1.nParentHash < vote2.nParentHash); - if(!fResult) { + if (!fResult) { return false; } fResult = fResult && (vote1.nParentHash == vote2.nParentHash); fResult = fResult && (vote1.nVoteOutcome < vote2.nVoteOutcome); - if(!fResult) { + if (!fResult) { return false; } fResult = fResult && (vote1.nVoteOutcome == vote2.nVoteOutcome); fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal); - if(!fResult) { + if (!fResult) { return false; } fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal); diff --git a/src/governance-vote.h b/src/governance-vote.h index 68772503cd01..5dc5814829f6 100644 --- a/src/governance-vote.h +++ b/src/governance-vote.h @@ -1,21 +1,21 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. + #ifndef GOVERNANCE_VOTE_H #define GOVERNANCE_VOTE_H #include "key.h" #include "primitives/transaction.h" - -#include +#include "bls/bls.h" class CGovernanceVote; class CConnman; // INTENTION OF MASTERNODES REGARDING ITEM -enum vote_outcome_enum_t { +enum vote_outcome_enum_t { VOTE_OUTCOME_NONE = 0, VOTE_OUTCOME_YES = 1, VOTE_OUTCOME_NO = 2, @@ -24,7 +24,7 @@ enum vote_outcome_enum_t { // SIGNAL VARIOUS THINGS TO HAPPEN: -enum vote_signal_enum_t { +enum vote_signal_enum_t { VOTE_SIGNAL_NONE = 0, VOTE_SIGNAL_FUNDING = 1, // -- fund this object for it's stated amount VOTE_SIGNAL_VALID = 2, // -- this object checks out in sentinel engine @@ -50,7 +50,7 @@ class CGovernanceVoting }; // -// CGovernanceVote - Allow a masternode node to vote and broadcast throughout the network +// CGovernanceVote - Allow a masternode to vote and broadcast throughout the network // class CGovernanceVote @@ -60,8 +60,8 @@ class CGovernanceVote friend bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2); private: - bool fValid; //if the vote is currently valid / counted - bool fSynced; //if we've sent this to our peers + bool fValid; //if the vote is currently valid / counted + bool fSynced; //if we've sent this to our peers int nVoteSignal; // see VOTE_ACTIONS above COutPoint masternodeOutpoint; uint256 nParentHash; @@ -83,25 +83,27 @@ class CGovernanceVote int64_t GetTimestamp() const { return nTime; } - vote_signal_enum_t GetSignal() const { return vote_signal_enum_t(nVoteSignal); } + vote_signal_enum_t GetSignal() const { return vote_signal_enum_t(nVoteSignal); } - vote_outcome_enum_t GetOutcome() const { return vote_outcome_enum_t(nVoteOutcome); } + vote_outcome_enum_t GetOutcome() const { return vote_outcome_enum_t(nVoteOutcome); } const uint256& GetParentHash() const { return nParentHash; } - void SetTime(int64_t nTimeIn) { nTime = nTimeIn; UpdateHash(); } + void SetTime(int64_t nTimeIn) + { + nTime = nTimeIn; + UpdateHash(); + } void SetSignature(const std::vector& vchSigIn) { vchSig = vchSigIn; } - bool Sign(const CKey& keyMasternode, const CPubKey& pubKeyMasternode); - bool CheckSignature(const CPubKey& pubKeyMasternode) const; - bool IsValid(bool fSignatureCheck) const; + bool Sign(const CKey& key, const CKeyID& keyID); + bool CheckSignature(const CKeyID& keyID) const; + bool Sign(const CBLSSecretKey& key); + bool CheckSignature(const CBLSPublicKey& pubKey) const; + bool IsValid(bool useVotingKey) const; void Relay(CConnman& connman) const; - std::string GetVoteString() const { - return CGovernanceVoting::ConvertOutcomeToString(GetOutcome()); - } - const COutPoint& GetMasternodeOutpoint() const { return masternodeOutpoint; } /** @@ -118,22 +120,9 @@ class CGovernanceVote ADD_SERIALIZE_METHODS; template - inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (nVersion == 70209 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin{}; - if (ser_action.ForRead()) { - READWRITE(txin); - masternodeOutpoint = txin.prevout; - } else { - txin = CTxIn(masternodeOutpoint); - READWRITE(txin); - } - } else { - // using new format directly - READWRITE(masternodeOutpoint); - } + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(masternodeOutpoint); READWRITE(nParentHash); READWRITE(nVoteOutcome); READWRITE(nVoteSignal); @@ -144,7 +133,6 @@ class CGovernanceVote if (ser_action.ForRead()) UpdateHash(); } - }; #endif diff --git a/src/governance-votedb.cpp b/src/governance-votedb.cpp index 4e5ce715a050..ba84de60df76 100644 --- a/src/governance-votedb.cpp +++ b/src/governance-votedb.cpp @@ -1,20 +1,21 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "governance-votedb.h" -CGovernanceObjectVoteFile::CGovernanceObjectVoteFile() - : nMemoryVotes(0), - listVotes(), - mapVoteIndex() -{} +CGovernanceObjectVoteFile::CGovernanceObjectVoteFile() : + nMemoryVotes(0), + listVotes(), + mapVoteIndex() +{ +} -CGovernanceObjectVoteFile::CGovernanceObjectVoteFile(const CGovernanceObjectVoteFile& other) - : nMemoryVotes(other.nMemoryVotes), - listVotes(other.listVotes), - mapVoteIndex() +CGovernanceObjectVoteFile::CGovernanceObjectVoteFile(const CGovernanceObjectVoteFile& other) : + nMemoryVotes(other.nMemoryVotes), + listVotes(other.listVotes), + mapVoteIndex() { RebuildIndex(); } @@ -38,7 +39,7 @@ bool CGovernanceObjectVoteFile::HasVote(const uint256& nHash) const bool CGovernanceObjectVoteFile::SerializeVoteToStream(const uint256& nHash, CDataStream& ss) const { vote_m_cit it = mapVoteIndex.find(nHash); - if(it == mapVoteIndex.end()) { + if (it == mapVoteIndex.end()) { return false; } ss << *(it->second); @@ -48,7 +49,7 @@ bool CGovernanceObjectVoteFile::SerializeVoteToStream(const uint256& nHash, CDat std::vector CGovernanceObjectVoteFile::GetVotes() const { std::vector vecResult; - for(vote_l_cit it = listVotes.begin(); it != listVotes.end(); ++it) { + for (vote_l_cit it = listVotes.begin(); it != listVotes.end(); ++it) { vecResult.push_back(*it); } return vecResult; @@ -57,13 +58,12 @@ std::vector CGovernanceObjectVoteFile::GetVotes() const void CGovernanceObjectVoteFile::RemoveVotesFromMasternode(const COutPoint& outpointMasternode) { vote_l_it it = listVotes.begin(); - while(it != listVotes.end()) { - if(it->GetMasternodeOutpoint() == outpointMasternode) { + while (it != listVotes.end()) { + if (it->GetMasternodeOutpoint() == outpointMasternode) { --nMemoryVotes; mapVoteIndex.erase(it->GetHash()); listVotes.erase(it++); - } - else { + } else { ++it; } } @@ -74,15 +74,14 @@ void CGovernanceObjectVoteFile::RebuildIndex() mapVoteIndex.clear(); nMemoryVotes = 0; vote_l_it it = listVotes.begin(); - while(it != listVotes.end()) { + while (it != listVotes.end()) { CGovernanceVote& vote = *it; uint256 nHash = vote.GetHash(); - if(mapVoteIndex.find(nHash) == mapVoteIndex.end()) { + if (mapVoteIndex.find(nHash) == mapVoteIndex.end()) { mapVoteIndex[nHash] = it; ++nMemoryVotes; ++it; - } - else { + } else { listVotes.erase(it++); } } diff --git a/src/governance-votedb.h b/src/governance-votedb.h index 0ad7c104f8d9..86e7558ae751 100644 --- a/src/governance-votedb.h +++ b/src/governance-votedb.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -31,7 +31,7 @@ class CGovernanceObjectVoteFile typedef vote_l_t::const_iterator vote_l_cit; - typedef std::map vote_m_t; + typedef std::map vote_m_t; typedef vote_m_t::iterator vote_m_it; @@ -66,7 +66,8 @@ class CGovernanceObjectVoteFile */ bool SerializeVoteToStream(const uint256& nHash, CDataStream& ss) const; - int GetVoteCount() { + int GetVoteCount() + { return nMemoryVotes; } @@ -81,13 +82,13 @@ class CGovernanceObjectVoteFile { READWRITE(nMemoryVotes); READWRITE(listVotes); - if(ser_action.ForRead()) { + if (ser_action.ForRead()) { RebuildIndex(); } } + private: void RebuildIndex(); - }; #endif diff --git a/src/governance.cpp b/src/governance.cpp index 2626f039b636..3f8dd20022c7 100644 --- a/src/governance.cpp +++ b/src/governance.cpp @@ -1,45 +1,47 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "consensus/validation.h" #include "governance.h" +#include "consensus/validation.h" +#include "governance-classes.h" #include "governance-object.h" #include "governance-validators.h" #include "governance-vote.h" -#include "governance-classes.h" -#include "net_processing.h" -#include "netmessagemaker.h" -#include "masternode.h" #include "masternode-sync.h" +#include "masternode.h" #include "masternodeman.h" #include "messagesigner.h" +#include "net_processing.h" #include "netfulfilledman.h" +#include "netmessagemaker.h" #include "util.h" +#include "validationinterface.h" CGovernanceManager governance; int nSubmittedFinalBudget; const std::string CGovernanceManager::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-13"; -const int CGovernanceManager::MAX_TIME_FUTURE_DEVIATION = 60*60; +const int CGovernanceManager::MAX_TIME_FUTURE_DEVIATION = 60 * 60; const int CGovernanceManager::RELIABLE_PROPAGATION_TIME = 60; -CGovernanceManager::CGovernanceManager() - : nTimeLastDiff(0), - nCachedBlockHeight(0), - mapObjects(), - mapErasedGovernanceObjects(), - mapMasternodeOrphanObjects(), - cmapVoteToObject(MAX_CACHE_SIZE), - cmapInvalidVotes(MAX_CACHE_SIZE), - cmmapOrphanVotes(MAX_CACHE_SIZE), - mapLastMasternodeObject(), - setRequestedObjects(), - fRateChecksEnabled(true), - cs() -{} +CGovernanceManager::CGovernanceManager() : + nTimeLastDiff(0), + nCachedBlockHeight(0), + mapObjects(), + mapErasedGovernanceObjects(), + mapMasternodeOrphanObjects(), + cmapVoteToObject(MAX_CACHE_SIZE), + cmapInvalidVotes(MAX_CACHE_SIZE), + cmmapOrphanVotes(MAX_CACHE_SIZE), + mapLastMasternodeObject(), + setRequestedObjects(), + fRateChecksEnabled(true), + cs() +{ +} // Accessors for thread-safe access to maps bool CGovernanceManager::HaveObjectForHash(const uint256& nHash) const @@ -65,7 +67,7 @@ bool CGovernanceManager::HaveVoteForHash(const uint256& nHash) const { LOCK(cs); - CGovernanceObject* pGovobj = NULL; + CGovernanceObject* pGovobj = nullptr; return cmapVoteToObject.Get(nHash, pGovobj) && pGovobj->GetVoteFile().HasVote(nHash); } @@ -79,25 +81,21 @@ bool CGovernanceManager::SerializeVoteForHash(const uint256& nHash, CDataStream& { LOCK(cs); - CGovernanceObject* pGovobj = NULL; - - return cmapVoteToObject.Get(nHash,pGovobj) && pGovobj->GetVoteFile().SerializeVoteToStream(nHash, ss); + CGovernanceObject* pGovobj = nullptr; + return cmapVoteToObject.Get(nHash, pGovobj) && pGovobj->GetVoteFile().SerializeVoteToStream(nHash, ss); } void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { // lite mode is not supported - if(fLiteMode) return; - if(!masternodeSync.IsBlockchainSynced()) return; + if (fLiteMode) return; + if (!masternodeSync.IsBlockchainSynced()) return; // ANOTHER USER IS ASKING US TO HELP THEM SYNC GOVERNANCE OBJECT DATA - if (strCommand == NetMsgType::MNGOVERNANCESYNC) - { - - if(pfrom->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) { + if (strCommand == NetMsgType::MNGOVERNANCESYNC) { + if (pfrom->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) { LogPrint("gobject", "MNGOVERNANCESYNC -- peer=%d using obsolete version %i\n", pfrom->id, pfrom->nVersion); - connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_GOVERNANCE_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_GOVERNANCE_PEER_PROTO_VERSION))); return; } @@ -111,15 +109,14 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm vRecv >> nProp; - if(pfrom->nVersion >= GOVERNANCE_FILTER_PROTO_VERSION) { + if (pfrom->nVersion >= GOVERNANCE_FILTER_PROTO_VERSION) { vRecv >> filter; filter.UpdateEmptyFull(); - } - else { + } else { filter.clear(); } - if(nProp == uint256()) { + if (nProp == uint256()) { SyncAll(pfrom, connman); } else { SyncSingleObjAndItsVotes(pfrom, nProp, filter, connman); @@ -128,8 +125,7 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm } // A NEW GOVERNANCE OBJECT HAS ARRIVED - else if (strCommand == NetMsgType::MNGOVERNANCEOBJECT) - { + else if (strCommand == NetMsgType::MNGOVERNANCEOBJECT) { // MAKE SURE WE HAVE A VALID REFERENCE TO THE TIP BEFORE CONTINUING CGovernanceObject govobj; @@ -139,14 +135,13 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm pfrom->setAskFor.erase(nHash); - if(pfrom->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) { LogPrint("gobject", "MNGOVERNANCEOBJECT -- peer=%d using obsolete version %i\n", pfrom->id, pfrom->nVersion); - connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_GOVERNANCE_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_GOVERNANCE_PEER_PROTO_VERSION))); return; } - if(!masternodeSync.IsMasternodeListSynced()) { + if (!masternodeSync.IsMasternodeListSynced()) { LogPrint("gobject", "MNGOVERNANCEOBJECT -- masternode list not synced\n"); return; } @@ -155,22 +150,22 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm LogPrint("gobject", "MNGOVERNANCEOBJECT -- Received object: %s\n", strHash); - if(!AcceptObjectMessage(nHash)) { + if (!AcceptObjectMessage(nHash)) { LogPrintf("MNGOVERNANCEOBJECT -- Received unrequested object: %s\n", strHash); return; } LOCK2(cs_main, cs); - if(mapObjects.count(nHash) || mapPostponedObjects.count(nHash) || - mapErasedGovernanceObjects.count(nHash) || mapMasternodeOrphanObjects.count(nHash)) { + if (mapObjects.count(nHash) || mapPostponedObjects.count(nHash) || + mapErasedGovernanceObjects.count(nHash) || mapMasternodeOrphanObjects.count(nHash)) { // TODO - print error code? what if it's GOVOBJ_ERROR_IMMATURE? LogPrint("gobject", "MNGOVERNANCEOBJECT -- Received already seen object: %s\n", strHash); return; } bool fRateCheckBypassed = false; - if(!MasternodeRateCheck(govobj, true, false, fRateCheckBypassed)) { + if (!MasternodeRateCheck(govobj, true, false, fRateCheckBypassed)) { LogPrintf("MNGOVERNANCEOBJECT -- masternode rate check failed - %s - (current block height %d) \n", strHash, nCachedBlockHeight); return; } @@ -182,16 +177,15 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm bool fMissingConfirmations = false; bool fIsValid = govobj.IsValidLocally(strError, fMasternodeMissing, fMissingConfirmations, true); - if(fRateCheckBypassed && (fIsValid || fMasternodeMissing)) { - if(!MasternodeRateCheck(govobj, true)) { + if (fRateCheckBypassed && (fIsValid || fMasternodeMissing)) { + if (!MasternodeRateCheck(govobj, true)) { LogPrintf("MNGOVERNANCEOBJECT -- masternode rate check failed (after signature verification) - %s - (current block height %d) \n", strHash, nCachedBlockHeight); return; } } - if(!fIsValid) { - if(fMasternodeMissing) { - + if (!fIsValid) { + if (fMasternodeMissing) { int& count = mapMasternodeOrphanCounter[govobj.GetMasternodeOutpoint()]; if (count >= 10) { LogPrint("gobject", "MNGOVERNANCEOBJECT -- Too many orphan objects, missing masternode=%s\n", govobj.GetMasternodeOutpoint().ToStringShort()); @@ -205,7 +199,7 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm ExpirationInfo info(pfrom->GetId(), GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME); mapMasternodeOrphanObjects.insert(std::make_pair(nHash, object_info_pair_t(govobj, info))); LogPrintf("MNGOVERNANCEOBJECT -- Missing masternode for: %s, strError = %s\n", strHash, strError); - } else if(fMissingConfirmations) { + } else if (fMissingConfirmations) { AddPostponedObject(govobj); LogPrintf("MNGOVERNANCEOBJECT -- Not enough fee confirmations for: %s, strError = %s\n", strHash, strError); } else { @@ -221,8 +215,7 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm } // A NEW GOVERNANCE OBJECT VOTE HAS ARRIVED - else if (strCommand == NetMsgType::MNGOVERNANCEOBJECTVOTE) - { + else if (strCommand == NetMsgType::MNGOVERNANCEOBJECTVOTE) { CGovernanceVote vote; vRecv >> vote; @@ -230,14 +223,13 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm pfrom->setAskFor.erase(nHash); - if(pfrom->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) { + if (pfrom->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) { LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- peer=%d using obsolete version %i\n", pfrom->id, pfrom->nVersion); - connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_GOVERNANCE_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_GOVERNANCE_PEER_PROTO_VERSION))); } // Ignore such messages until masternode list is synced - if(!masternodeSync.IsMasternodeListSynced()) { + if (!masternodeSync.IsMasternodeListSynced()) { LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- masternode list not synced\n"); return; } @@ -246,27 +238,27 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm std::string strHash = nHash.ToString(); - if(!AcceptVoteMessage(nHash)) { + if (!AcceptVoteMessage(nHash)) { LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- Received unrequested vote object: %s, hash: %s, peer = %d\n", - vote.ToString(), strHash, pfrom->GetId()); + vote.ToString(), strHash, pfrom->GetId()); return; } CGovernanceException exception; - if(ProcessVote(pfrom, vote, exception, connman)) { + if (ProcessVote(pfrom, vote, exception, connman)) { LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- %s new\n", strHash); masternodeSync.BumpAssetLastTime("MNGOVERNANCEOBJECTVOTE"); vote.Relay(connman); - } - else { + } else { LogPrint("gobject", "MNGOVERNANCEOBJECTVOTE -- Rejected vote, error = %s\n", exception.what()); - if((exception.GetNodePenalty() != 0) && masternodeSync.IsSynced()) { + if ((exception.GetNodePenalty() != 0) && masternodeSync.IsSynced()) { LOCK(cs_main); Misbehaving(pfrom->GetId(), exception.GetNodePenalty()); } return; } - + // SEND NOTIFICATION TO SCRIPT/ZMQ + GetMainSignals().NotifyGovernanceVote(vote); } } @@ -279,19 +271,17 @@ void CGovernanceManager::CheckOrphanVotes(CGovernanceObject& govobj, CGovernance ScopedLockBool guard(cs, fRateChecksEnabled, false); int64_t nNow = GetAdjustedTime(); - for(size_t i = 0; i < vecVotePairs.size(); ++i) { + for (auto& pairVote : vecVotePairs) { bool fRemove = false; - vote_time_pair_t& pairVote = vecVotePairs[i]; CGovernanceVote& vote = pairVote.first; CGovernanceException exception; - if(pairVote.second < nNow) { + if (pairVote.second < nNow) { fRemove = true; - } - else if(govobj.ProcessVote(NULL, vote, exception, connman)) { + } else if (govobj.ProcessVote(nullptr, vote, exception, connman)) { vote.Relay(connman); fRemove = true; } - if(fRemove) { + if (fRemove) { cmmapOrphanVotes.Erase(nHash, pairVote); } } @@ -299,7 +289,7 @@ void CGovernanceManager::CheckOrphanVotes(CGovernanceObject& govobj, CGovernance void CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj, CConnman& connman, CNode* pfrom) { - DBG( std::cout << "CGovernanceManager::AddGovernanceObject START" << std::endl; ); + DBG(std::cout << "CGovernanceManager::AddGovernanceObject START" << std::endl;); uint256 nHash = govobj.GetHash(); std::string strHash = nHash.ToString(); @@ -313,7 +303,7 @@ void CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj, CConnman // MAKE SURE THIS OBJECT IS OK - if(!govobj.IsValidLocally(strError, true)) { + if (!govobj.IsValidLocally(strError, true)) { LogPrintf("CGovernanceManager::AddGovernanceObject -- invalid governance object - %s - (nCachedBlockHeight %d) \n", strError, nCachedBlockHeight); return; } @@ -324,20 +314,20 @@ void CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj, CConnman // IF WE HAVE THIS OBJECT ALREADY, WE DON'T WANT ANOTHER COPY auto objpair = mapObjects.emplace(nHash, govobj); - if(!objpair.second) { + if (!objpair.second) { LogPrintf("CGovernanceManager::AddGovernanceObject -- already have governance object %s\n", nHash.ToString()); return; } // SHOULD WE ADD THIS OBJECT TO ANY OTHER MANANGERS? - DBG( std::cout << "CGovernanceManager::AddGovernanceObject Before trigger block, GetDataAsPlainString = " - << govobj.GetDataAsPlainString() - << ", nObjectType = " << govobj.nObjectType - << std::endl; ); + DBG(std::cout << "CGovernanceManager::AddGovernanceObject Before trigger block, GetDataAsPlainString = " + << govobj.GetDataAsPlainString() + << ", nObjectType = " << govobj.nObjectType + << std::endl;); if (govobj.nObjectType == GOVERNANCE_OBJECT_TRIGGER) { - DBG( std::cout << "CGovernanceManager::AddGovernanceObject Before AddNewTrigger" << std::endl; ); + DBG(std::cout << "CGovernanceManager::AddGovernanceObject Before AddNewTrigger" << std::endl;); if (!triggerman.AddNewTrigger(nHash)) { LogPrint("gobject", "CGovernanceManager::AddGovernanceObject -- undo adding invalid trigger object: hash = %s\n", nHash.ToString()); CGovernanceObject& objref = objpair.first->second; @@ -347,10 +337,10 @@ void CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj, CConnman } return; } - DBG( std::cout << "CGovernanceManager::AddGovernanceObject After AddNewTrigger" << std::endl; ); + DBG(std::cout << "CGovernanceManager::AddGovernanceObject After AddNewTrigger" << std::endl;); } - LogPrintf("CGovernanceManager::AddGovernanceObject -- %s new, received from %s\n", strHash, pfrom? pfrom->GetAddrName() : "NULL"); + LogPrintf("CGovernanceManager::AddGovernanceObject -- %s new, received from %s\n", strHash, pfrom ? pfrom->GetAddrName() : "nullptr"); govobj.Relay(connman); // Update the rate buffer @@ -363,7 +353,11 @@ void CGovernanceManager::AddGovernanceObject(CGovernanceObject& govobj, CConnman CGovernanceException exception; CheckOrphanVotes(govobj, exception, connman); - DBG( std::cout << "CGovernanceManager::AddGovernanceObject END" << std::endl; ); + // SEND NOTIFICATION TO SCRIPT/ZMQ + GetMainSignals().NotifyGovernanceObject(govobj); + + + DBG(std::cout << "CGovernanceManager::AddGovernanceObject END" << std::endl;); } void CGovernanceManager::UpdateCachesAndClean() @@ -374,9 +368,9 @@ void CGovernanceManager::UpdateCachesAndClean() LOCK2(cs_main, cs); - for(size_t i = 0; i < vecDirtyHashes.size(); ++i) { - object_m_it it = mapObjects.find(vecDirtyHashes[i]); - if(it == mapObjects.end()) { + for (const uint256& nHash : vecDirtyHashes) { + object_m_it it = mapObjects.find(nHash); + if (it == mapObjects.end()) { continue; } it->second.ClearMasternodeVotes(); @@ -391,11 +385,10 @@ void CGovernanceManager::UpdateCachesAndClean() object_m_it it = mapObjects.begin(); int64_t nNow = GetAdjustedTime(); - while(it != mapObjects.end()) - { + while (it != mapObjects.end()) { CGovernanceObject* pObj = &((*it).second); - if(!pObj) { + if (!pObj) { ++it; continue; } @@ -404,7 +397,7 @@ void CGovernanceManager::UpdateCachesAndClean() std::string strHash = nHash.ToString(); // IF CACHE IS NOT DIRTY, WHY DO THIS? - if(pObj->IsSetDirtyCache()) { + if (pObj->IsSetDirtyCache()) { // UPDATE LOCAL VALIDITY AGAINST CRYPTO DATA pObj->UpdateLocalValidity(); @@ -417,30 +410,29 @@ void CGovernanceManager::UpdateCachesAndClean() int64_t nTimeSinceDeletion = nNow - pObj->GetDeletionTime(); LogPrint("gobject", "CGovernanceManager::UpdateCachesAndClean -- Checking object for deletion: %s, deletion time = %d, time since deletion = %d, delete flag = %d, expired flag = %d\n", - strHash, pObj->GetDeletionTime(), nTimeSinceDeletion, pObj->IsSetCachedDelete(), pObj->IsSetExpired()); + strHash, pObj->GetDeletionTime(), nTimeSinceDeletion, pObj->IsSetCachedDelete(), pObj->IsSetExpired()); - if((pObj->IsSetCachedDelete() || pObj->IsSetExpired()) && - (nTimeSinceDeletion >= GOVERNANCE_DELETION_DELAY)) { + if ((pObj->IsSetCachedDelete() || pObj->IsSetExpired()) && + (nTimeSinceDeletion >= GOVERNANCE_DELETION_DELAY)) { LogPrintf("CGovernanceManager::UpdateCachesAndClean -- erase obj %s\n", (*it).first.ToString()); mnodeman.RemoveGovernanceObject(pObj->GetHash()); // Remove vote references const object_ref_cm_t::list_t& listItems = cmapVoteToObject.GetItemList(); object_ref_cm_t::list_cit lit = listItems.begin(); - while(lit != listItems.end()) { - if(lit->value == pObj) { + while (lit != listItems.end()) { + if (lit->value == pObj) { uint256 nKey = lit->key; ++lit; cmapVoteToObject.Erase(nKey); - } - else { + } else { ++lit; } } int64_t nTimeExpired{0}; - if(pObj->GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { + if (pObj->GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { // keep hashes of deleted proposals forever nTimeExpired = std::numeric_limits::max(); } else { @@ -468,11 +460,12 @@ void CGovernanceManager::UpdateCachesAndClean() // forget about expired deleted objects hash_time_m_it s_it = mapErasedGovernanceObjects.begin(); - while(s_it != mapErasedGovernanceObjects.end()) { - if(s_it->second < nNow) + while (s_it != mapErasedGovernanceObjects.end()) { + if (s_it->second < nNow) { mapErasedGovernanceObjects.erase(s_it++); - else + } else { ++s_it; + } } LogPrintf("CGovernanceManager::UpdateCachesAndClean -- %s\n", ToString()); @@ -482,10 +475,9 @@ CGovernanceObject* CGovernanceManager::FindGovernanceObject(const uint256& nHash { LOCK(cs); - if(mapObjects.count(nHash)) - return &mapObjects[nHash]; + if (mapObjects.count(nHash)) return &mapObjects[nHash]; - return NULL; + return nullptr; } std::vector CGovernanceManager::GetMatchingVotes(const uint256& nParentHash) const @@ -494,7 +486,7 @@ std::vector CGovernanceManager::GetMatchingVotes(const uint256& std::vector vecResult; object_m_cit it = mapObjects.find(nParentHash); - if(it == mapObjects.end()) { + if (it == mapObjects.end()) { return vecResult; } @@ -508,28 +500,27 @@ std::vector CGovernanceManager::GetCurrentVotes(const uint256& // Find the governance object or short-circuit. object_m_cit it = mapObjects.find(nParentHash); - if(it == mapObjects.end()) return vecResult; + if (it == mapObjects.end()) return vecResult; const CGovernanceObject& govobj = it->second; CMasternode mn; std::map mapMasternodes; - if(mnCollateralOutpointFilter.IsNull()) { + if (mnCollateralOutpointFilter.IsNull()) { mapMasternodes = mnodeman.GetFullMasternodeMap(); } else if (mnodeman.Get(mnCollateralOutpointFilter, mn)) { mapMasternodes[mnCollateralOutpointFilter] = mn; } // Loop thru each MN collateral outpoint and get the votes for the `nParentHash` governance object - for (const auto& mnpair : mapMasternodes) - { + for (const auto& mnpair : mapMasternodes) { // get a vote_rec_t from the govobj vote_rec_t voteRecord; if (!govobj.GetCurrentMNVotes(mnpair.first, voteRecord)) continue; - for (vote_instance_m_it it3 = voteRecord.mapInstances.begin(); it3 != voteRecord.mapInstances.end(); ++it3) { - int signal = (it3->first); - int outcome = ((it3->second).eOutcome); - int64_t nCreationTime = ((it3->second).nCreationTime); + for (const auto& voteInstancePair : voteRecord.mapInstances) { + int signal = voteInstancePair.first; + int outcome = voteInstancePair.second.eOutcome; + int64_t nCreationTime = voteInstancePair.second.nCreationTime; CGovernanceVote vote = CGovernanceVote(mnpair.first, nParentHash, (vote_signal_enum_t)signal, (vote_outcome_enum_t)outcome); vote.SetTime(nCreationTime); @@ -547,24 +538,15 @@ std::vector CGovernanceManager::GetAllNewerThan(int64_ std::vector vGovObjs; - object_m_cit it = mapObjects.begin(); - while(it != mapObjects.end()) - { + for (const auto& objPair : mapObjects) { // IF THIS OBJECT IS OLDER THAN TIME, CONTINUE - - if((*it).second.GetCreationTime() < nMoreThanTime) { - ++it; + if (objPair.second.GetCreationTime() < nMoreThanTime) { continue; } // ADD GOVERNANCE OBJECT TO LIST - - const CGovernanceObject* pGovObj = &((*it).second); + const CGovernanceObject* pGovObj = &(objPair.second); vGovObjs.push_back(pGovObj); - - // NEXT - - ++it; } return vGovObjs; @@ -574,16 +556,16 @@ std::vector CGovernanceManager::GetAllNewerThan(int64_ // Sort by votes, if there's a tie sort by their feeHash TX // struct sortProposalsByVotes { - bool operator()(const std::pair &left, const std::pair &right) { - if (left.second != right.second) - return (left.second > right.second); + bool operator()(const std::pair& left, const std::pair& right) + { + if (left.second != right.second) return (left.second > right.second); return (UintToArith256(left.first->GetCollateralHash()) > UintToArith256(right.first->GetCollateralHash())); } }; void CGovernanceManager::DoMaintenance(CConnman& connman) { - if(fLiteMode || !masternodeSync.IsSynced()) return; + if (fLiteMode || !masternodeSync.IsSynced()) return; // CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES @@ -599,38 +581,36 @@ void CGovernanceManager::DoMaintenance(CConnman& connman) bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv) { // do not request objects until it's time to sync - if(!masternodeSync.IsWinnersListSynced()) return false; + if (!masternodeSync.IsWinnersListSynced()) return false; LOCK(cs); LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest inv = %s\n", inv.ToString()); // First check if we've already recorded this object - switch(inv.type) { - case MSG_GOVERNANCE_OBJECT: - { - if(mapObjects.count(inv.hash) == 1 || mapPostponedObjects.count(inv.hash) == 1) { + switch (inv.type) { + case MSG_GOVERNANCE_OBJECT: { + if (mapObjects.count(inv.hash) == 1 || mapPostponedObjects.count(inv.hash) == 1) { LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest already have governance object, returning false\n"); return false; } + break; } - break; - case MSG_GOVERNANCE_OBJECT_VOTE: - { - if(cmapVoteToObject.HasKey(inv.hash)) { + case MSG_GOVERNANCE_OBJECT_VOTE: { + if (cmapVoteToObject.HasKey(inv.hash)) { LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest already have governance vote, returning false\n"); return false; } + break; } - break; default: LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest unknown type, returning false\n"); return false; } - hash_s_t* setHash = NULL; - switch(inv.type) { + hash_s_t* setHash = nullptr; + switch (inv.type) { case MSG_GOVERNANCE_OBJECT: setHash = &setRequestedObjects; break; @@ -642,7 +622,7 @@ bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv) } hash_s_cit it = setHash->find(inv.hash); - if(it == setHash->end()) { + if (it == setHash->end()) { setHash->insert(inv.hash); LogPrint("gobject", "CGovernanceManager::ConfirmInventoryRequest added inv to requested set\n"); } @@ -654,7 +634,7 @@ bool CGovernanceManager::ConfirmInventoryRequest(const CInv& inv) void CGovernanceManager::SyncSingleObjAndItsVotes(CNode* pnode, const uint256& nProp, const CBloomFilter& filter, CConnman& connman) { // do not provide any data until our node is synced - if(!masternodeSync.IsSynced()) return; + if (!masternodeSync.IsSynced()) return; int nVoteCount = 0; @@ -666,7 +646,7 @@ void CGovernanceManager::SyncSingleObjAndItsVotes(CNode* pnode, const uint256& n // single valid object and its valid votes object_m_it it = mapObjects.find(nProp); - if(it == mapObjects.end()) { + if (it == mapObjects.end()) { LogPrint("gobject", "CGovernanceManager::%s -- no matching object for hash %s, peer=%d\n", __func__, nProp.ToString(), pnode->id); return; } @@ -675,9 +655,9 @@ void CGovernanceManager::SyncSingleObjAndItsVotes(CNode* pnode, const uint256& n LogPrint("gobject", "CGovernanceManager::%s -- attempting to sync govobj: %s, peer=%d\n", __func__, strHash, pnode->id); - if(govobj.IsSetCachedDelete() || govobj.IsSetExpired()) { + if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) { LogPrintf("CGovernanceManager::%s -- not syncing deleted/expired govobj: %s, peer=%d\n", __func__, - strHash, pnode->id); + strHash, pnode->id); return; } @@ -689,7 +669,10 @@ void CGovernanceManager::SyncSingleObjAndItsVotes(CNode* pnode, const uint256& n for (const auto& vote : fileVotes.GetVotes()) { uint256 nVoteHash = vote.GetHash(); - if(filter.contains(nVoteHash) || !vote.IsValid(true)) { + + bool onlyOwnerAllowed = govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL; + + if (filter.contains(nVoteHash) || !vote.IsValid(onlyOwnerAllowed)) { continue; } pnode->PushInventory(CInv(MSG_GOVERNANCE_OBJECT_VOTE, nVoteHash)); @@ -705,9 +688,9 @@ void CGovernanceManager::SyncSingleObjAndItsVotes(CNode* pnode, const uint256& n void CGovernanceManager::SyncAll(CNode* pnode, CConnman& connman) const { // do not provide any data until our node is synced - if(!masternodeSync.IsSynced()) return; + if (!masternodeSync.IsSynced()) return; - if(netfulfilledman.HasFulfilledRequest(pnode->addr, NetMsgType::MNGOVERNANCESYNC)) { + if (netfulfilledman.HasFulfilledRequest(pnode->addr, NetMsgType::MNGOVERNANCESYNC)) { LOCK(cs_main); // Asking for the whole list multiple times in a short period of time is no good LogPrint("gobject", "CGovernanceManager::%s -- peer already asked me for the list\n", __func__); @@ -726,21 +709,22 @@ void CGovernanceManager::SyncAll(CNode* pnode, CConnman& connman) const LOCK2(cs_main, cs); // all valid objects, no votes - for(object_m_cit it = mapObjects.begin(); it != mapObjects.end(); ++it) { - const CGovernanceObject& govobj = it->second; - std::string strHash = it->first.ToString(); + for (const auto& objPair : mapObjects) { + uint256 nHash = objPair.first; + const CGovernanceObject& govobj = objPair.second; + std::string strHash = nHash.ToString(); LogPrint("gobject", "CGovernanceManager::%s -- attempting to sync govobj: %s, peer=%d\n", __func__, strHash, pnode->id); - if(govobj.IsSetCachedDelete() || govobj.IsSetExpired()) { + if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) { LogPrintf("CGovernanceManager::%s -- not syncing deleted/expired govobj: %s, peer=%d\n", __func__, - strHash, pnode->id); + strHash, pnode->id); continue; } // Push the inventory budget proposal message over to the other client LogPrint("gobject", "CGovernanceManager::%s -- syncing govobj: %s, peer=%d\n", __func__, strHash, pnode->id); - pnode->PushInventory(CInv(MSG_GOVERNANCE_OBJECT, it->first)); + pnode->PushInventory(CInv(MSG_GOVERNANCE_OBJECT, nHash)); ++nObjCount; } @@ -752,14 +736,14 @@ void CGovernanceManager::SyncAll(CNode* pnode, CConnman& connman) const void CGovernanceManager::MasternodeRateUpdate(const CGovernanceObject& govobj) { - if(govobj.GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) - return; + if (govobj.GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) return; const COutPoint& masternodeOutpoint = govobj.GetMasternodeOutpoint(); - txout_m_it it = mapLastMasternodeObject.find(masternodeOutpoint); + txout_m_it it = mapLastMasternodeObject.find(masternodeOutpoint); - if(it == mapLastMasternodeObject.end()) + if (it == mapLastMasternodeObject.end()) { it = mapLastMasternodeObject.insert(txout_m_t::value_type(masternodeOutpoint, last_object_rec(true))).first; + } int64_t nTimestamp = govobj.GetCreationTime(); it->second.triggerBuffer.AddTimestamp(nTimestamp); @@ -784,15 +768,11 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo fRateCheckBypassed = false; - if(!masternodeSync.IsSynced()) { + if (!masternodeSync.IsSynced() || !fRateChecksEnabled) { return true; } - if(!fRateChecksEnabled) { - return true; - } - - if(govobj.GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) { + if (govobj.GetObjectType() != GOVERNANCE_OBJECT_TRIGGER) { return true; } @@ -803,23 +783,22 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo std::string strHash = govobj.GetHash().ToString(); - if(nTimestamp < nNow - 2 * nSuperblockCycleSeconds) { + if (nTimestamp < nNow - 2 * nSuperblockCycleSeconds) { LogPrintf("CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too old timestamp, masternode = %s, timestamp = %d, current time = %d\n", - strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow); + strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow); return false; } - if(nTimestamp > nNow + MAX_TIME_FUTURE_DEVIATION) { + if (nTimestamp > nNow + MAX_TIME_FUTURE_DEVIATION) { LogPrintf("CGovernanceManager::MasternodeRateCheck -- object %s rejected due to too new (future) timestamp, masternode = %s, timestamp = %d, current time = %d\n", - strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow); + strHash, masternodeOutpoint.ToStringShort(), nTimestamp, nNow); return false; } - txout_m_it it = mapLastMasternodeObject.find(masternodeOutpoint); - if(it == mapLastMasternodeObject.end()) - return true; + txout_m_it it = mapLastMasternodeObject.find(masternodeOutpoint); + if (it == mapLastMasternodeObject.end()) return true; - if(it->second.fStatusOK && !fForce) { + if (it->second.fStatusOK && !fForce) { fRateCheckBypassed = true; return true; } @@ -833,15 +812,16 @@ bool CGovernanceManager::MasternodeRateCheck(const CGovernanceObject& govobj, bo buffer.AddTimestamp(nTimestamp); double dRate = buffer.GetRate(); - if(dRate < dMaxRate) { + if (dRate < dMaxRate) { return true; } LogPrintf("CGovernanceManager::MasternodeRateCheck -- Rate too high: object hash = %s, masternode = %s, object timestamp = %d, rate = %f, max rate = %f\n", - strHash, masternodeOutpoint.ToStringShort(), nTimestamp, dRate, dMaxRate); + strHash, masternodeOutpoint.ToStringShort(), nTimestamp, dRate, dMaxRate); - if (fUpdateFailStatus) + if (fUpdateFailStatus) { it->second.fStatusOK = false; + } return false; } @@ -852,17 +832,17 @@ bool CGovernanceManager::ProcessVote(CNode* pfrom, const CGovernanceVote& vote, uint256 nHashVote = vote.GetHash(); uint256 nHashGovobj = vote.GetParentHash(); - if(cmapVoteToObject.HasKey(nHashVote)) { + if (cmapVoteToObject.HasKey(nHashVote)) { LogPrint("gobject", "CGovernanceObject::ProcessVote -- skipping known valid vote %s for object %s\n", nHashVote.ToString(), nHashGovobj.ToString()); LEAVE_CRITICAL_SECTION(cs); return false; } - if(cmapInvalidVotes.HasKey(nHashVote)) { + if (cmapInvalidVotes.HasKey(nHashVote)) { std::ostringstream ostr; ostr << "CGovernanceManager::ProcessVote -- Old invalid vote " - << ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort() - << ", governance object hash = " << nHashGovobj.ToString(); + << ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort() + << ", governance object hash = " << nHashGovobj.ToString(); LogPrintf("%s\n", ostr.str()); exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_PERMANENT_ERROR, 20); LEAVE_CRITICAL_SECTION(cs); @@ -870,12 +850,12 @@ bool CGovernanceManager::ProcessVote(CNode* pfrom, const CGovernanceVote& vote, } object_m_it it = mapObjects.find(nHashGovobj); - if(it == mapObjects.end()) { + if (it == mapObjects.end()) { std::ostringstream ostr; ostr << "CGovernanceManager::ProcessVote -- Unknown parent object " << nHashGovobj.ToString() << ", MN outpoint = " << vote.GetMasternodeOutpoint().ToStringShort(); exception = CGovernanceException(ostr.str(), GOVERNANCE_EXCEPTION_WARNING); - if(cmmapOrphanVotes.Insert(nHashGovobj, vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME))) { + if (cmmapOrphanVotes.Insert(nHashGovobj, vote_time_pair_t(vote, GetAdjustedTime() + GOVERNANCE_ORPHAN_EXPIRATION_TIME))) { LEAVE_CRITICAL_SECTION(cs); RequestGovernanceObject(pfrom, nHashGovobj, connman); LogPrintf("%s\n", ostr.str()); @@ -889,7 +869,7 @@ bool CGovernanceManager::ProcessVote(CNode* pfrom, const CGovernanceVote& vote, CGovernanceObject& govobj = it->second; - if(govobj.IsSetCachedDelete() || govobj.IsSetExpired()) { + if (govobj.IsSetCachedDelete() || govobj.IsSetExpired()) { LogPrint("gobject", "CGovernanceObject::ProcessVote -- ignoring vote for expired or deleted object, hash = %s\n", nHashGovobj.ToString()); LEAVE_CRITICAL_SECTION(cs); return false; @@ -906,8 +886,8 @@ void CGovernanceManager::CheckMasternodeOrphanVotes(CConnman& connman) ScopedLockBool guard(cs, fRateChecksEnabled, false); - for(object_m_it it = mapObjects.begin(); it != mapObjects.end(); ++it) { - it->second.CheckOrphanVotes(connman); + for (auto& objPair : mapObjects) { + objPair.second.CheckOrphanVotes(connman); } } @@ -917,19 +897,19 @@ void CGovernanceManager::CheckMasternodeOrphanObjects(CConnman& connman) int64_t nNow = GetAdjustedTime(); ScopedLockBool guard(cs, fRateChecksEnabled, false); object_info_m_it it = mapMasternodeOrphanObjects.begin(); - while(it != mapMasternodeOrphanObjects.end()) { + while (it != mapMasternodeOrphanObjects.end()) { object_info_pair_t& pair = it->second; CGovernanceObject& govobj = pair.first; - if(pair.second.nExpirationTime >= nNow) { + if (pair.second.nExpirationTime >= nNow) { std::string strError; bool fMasternodeMissing = false; bool fConfirmationsMissing = false; bool fIsValid = govobj.IsValidLocally(strError, fMasternodeMissing, fConfirmationsMissing, true); - if(fIsValid) { + if (fIsValid) { AddGovernanceObject(govobj, connman); - } else if(fMasternodeMissing) { + } else if (fMasternodeMissing) { ++it; continue; } @@ -939,7 +919,7 @@ void CGovernanceManager::CheckMasternodeOrphanObjects(CConnman& connman) } auto it_count = mapMasternodeOrphanCounter.find(govobj.GetMasternodeOutpoint()); - if(--it_count->second == 0) + if (--it_count->second == 0) mapMasternodeOrphanCounter.erase(it_count); mapMasternodeOrphanObjects.erase(it++); @@ -948,13 +928,12 @@ void CGovernanceManager::CheckMasternodeOrphanObjects(CConnman& connman) void CGovernanceManager::CheckPostponedObjects(CConnman& connman) { - if(!masternodeSync.IsSynced()) return; + if (!masternodeSync.IsSynced()) return; LOCK2(cs_main, cs); // Check postponed proposals - for(object_m_it it = mapPostponedObjects.begin(); it != mapPostponedObjects.end();) { - + for (object_m_it it = mapPostponedObjects.begin(); it != mapPostponedObjects.end();) { const uint256& nHash = it->first; CGovernanceObject& govobj = it->second; @@ -962,14 +941,14 @@ void CGovernanceManager::CheckPostponedObjects(CConnman& connman) std::string strError; bool fMissingConfirmations; - if (govobj.IsCollateralValid(strError, fMissingConfirmations)) - { - if(govobj.IsValidLocally(strError, false)) + if (govobj.IsCollateralValid(strError, fMissingConfirmations)) { + if (govobj.IsValidLocally(strError, false)) { AddGovernanceObject(govobj, connman); - else + } else { LogPrintf("CGovernanceManager::CheckPostponedObjects -- %s invalid\n", nHash.ToString()); + } - } else if(fMissingConfirmations) { + } else if (fMissingConfirmations) { // wait for more confirmations ++it; continue; @@ -984,11 +963,9 @@ void CGovernanceManager::CheckPostponedObjects(CConnman& connman) int64_t nNow = GetAdjustedTime(); int64_t nSuperblockCycleSeconds = Params().GetConsensus().nSuperblockCycle * Params().GetConsensus().nPowTargetSpacing; - for(hash_s_it it = setAdditionalRelayObjects.begin(); it != setAdditionalRelayObjects.end();) { - + for (hash_s_it it = setAdditionalRelayObjects.begin(); it != setAdditionalRelayObjects.end();) { object_m_it itObject = mapObjects.find(*it); - if(itObject != mapObjects.end()) { - + if (itObject != mapObjects.end()) { CGovernanceObject& govobj = itObject->second; int64_t nTimestamp = govobj.GetCreationTime(); @@ -996,8 +973,8 @@ void CGovernanceManager::CheckPostponedObjects(CConnman& connman) bool fValid = (nTimestamp <= nNow + MAX_TIME_FUTURE_DEVIATION) && (nTimestamp >= nNow - 2 * nSuperblockCycleSeconds); bool fReady = (nTimestamp <= nNow + MAX_TIME_FUTURE_DEVIATION - RELIABLE_PROPAGATION_TIME); - if(fValid) { - if(fReady) { + if (fValid) { + if (fReady) { LogPrintf("CGovernanceManager::CheckPostponedObjects -- additional relay: hash = %s\n", govobj.GetHash().ToString()); govobj.Relay(connman); } else { @@ -1016,15 +993,15 @@ void CGovernanceManager::CheckPostponedObjects(CConnman& connman) void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nHash, CConnman& connman, bool fUseFilter) { - if(!pfrom) { + if (!pfrom) { return; } - LogPrint("gobject", "CGovernanceObject::RequestGovernanceObject -- hash = %s (peer=%d)\n", nHash.ToString(), pfrom->GetId()); + LogPrint("gobject", "CGovernanceManager::RequestGovernanceObject -- nHash %s peer=%d\n", nHash.ToString(), pfrom->GetId()); CNetMsgMaker msgMaker(pfrom->GetSendVersion()); - if(pfrom->nVersion < GOVERNANCE_FILTER_PROTO_VERSION) { + if (pfrom->nVersion < GOVERNANCE_FILTER_PROTO_VERSION) { connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNGOVERNANCESYNC, nHash)); return; } @@ -1033,16 +1010,16 @@ void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nH filter.clear(); int nVoteCount = 0; - if(fUseFilter) { + if (fUseFilter) { LOCK(cs); CGovernanceObject* pObj = FindGovernanceObject(nHash); - if(pObj) { + if (pObj) { filter = CBloomFilter(Params().GetConsensus().nGovernanceFilterElements, GOVERNANCE_FILTER_FP_RATE, GetRandInt(999999), BLOOM_UPDATE_ALL); std::vector vecVotes = pObj->GetVoteFile().GetVotes(); nVoteCount = vecVotes.size(); - for(size_t i = 0; i < vecVotes.size(); ++i) { - filter.insert(vecVotes[i].GetHash()); + for (const auto& vote : vecVotes) { + filter.insert(vote.GetHash()); } } } @@ -1053,7 +1030,7 @@ void CGovernanceManager::RequestGovernanceObject(CNode* pfrom, const uint256& nH int CGovernanceManager::RequestGovernanceObjectVotes(CNode* pnode, CConnman& connman) { - if(pnode->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) return -3; + if (pnode->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) return -3; std::vector vNodesCopy; vNodesCopy.push_back(pnode); return RequestGovernanceObjectVotes(vNodesCopy, connman); @@ -1063,14 +1040,14 @@ int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& { static std::map > mapAskedRecently; - if(vNodesCopy.empty()) return -1; + if (vNodesCopy.empty()) return -1; int64_t nNow = GetTime(); int nTimeout = 60 * 60; size_t nPeersPerHashMax = 3; - std::vector vpGovObjsTmp; - std::vector vpGovObjsTriggersTmp; + std::vector vTriggerObjHashes; + std::vector vOtherObjHashes; // This should help us to get some idea about an impact this can bring once deployed on mainnet. // Testnet is ~40 times smaller in masternode count, but only ~1000 masternodes usually vote, @@ -1079,52 +1056,53 @@ int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& // On mainnet nMaxObjRequestsPerNode is always set to 1. int nMaxObjRequestsPerNode = 1; size_t nProjectedVotes = 2000; - if(Params().NetworkIDString() != CBaseChainParams::MAIN) { + if (Params().NetworkIDString() != CBaseChainParams::MAIN) { nMaxObjRequestsPerNode = std::max(1, int(nProjectedVotes / std::max(1, mnodeman.size()))); } { LOCK2(cs_main, cs); - if(mapObjects.empty()) return -2; + if (mapObjects.empty()) return -2; - for(object_m_it it = mapObjects.begin(); it != mapObjects.end(); ++it) { - if(mapAskedRecently.count(it->first)) { - std::map::iterator it1 = mapAskedRecently[it->first].begin(); - while(it1 != mapAskedRecently[it->first].end()) { - if(it1->second < nNow) { - mapAskedRecently[it->first].erase(it1++); + for (const auto& objPair : mapObjects) { + uint256 nHash = objPair.first; + if (mapAskedRecently.count(nHash)) { + auto it = mapAskedRecently[nHash].begin(); + while (it != mapAskedRecently[nHash].end()) { + if (it->second < nNow) { + mapAskedRecently[nHash].erase(it++); } else { - ++it1; + ++it; } } - if(mapAskedRecently[it->first].size() >= nPeersPerHashMax) continue; + if (mapAskedRecently[nHash].size() >= nPeersPerHashMax) continue; } - if(it->second.nObjectType == GOVERNANCE_OBJECT_TRIGGER) { - vpGovObjsTriggersTmp.push_back(&(it->second)); + + if (objPair.second.nObjectType == GOVERNANCE_OBJECT_TRIGGER) { + vTriggerObjHashes.push_back(nHash); } else { - vpGovObjsTmp.push_back(&(it->second)); + vOtherObjHashes.push_back(nHash); } } } - LogPrint("gobject", "CGovernanceManager::RequestGovernanceObjectVotes -- start: vpGovObjsTriggersTmp %d vpGovObjsTmp %d mapAskedRecently %d\n", - vpGovObjsTriggersTmp.size(), vpGovObjsTmp.size(), mapAskedRecently.size()); + LogPrint("gobject", "CGovernanceManager::RequestGovernanceObjectVotes -- start: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); FastRandomContext insecure_rand; - // shuffle pointers - std::random_shuffle(vpGovObjsTriggersTmp.begin(), vpGovObjsTriggersTmp.end(), insecure_rand); - std::random_shuffle(vpGovObjsTmp.begin(), vpGovObjsTmp.end(), insecure_rand); + std::random_shuffle(vTriggerObjHashes.begin(), vTriggerObjHashes.end(), insecure_rand); + std::random_shuffle(vOtherObjHashes.begin(), vOtherObjHashes.end(), insecure_rand); for (int i = 0; i < nMaxObjRequestsPerNode; ++i) { uint256 nHashGovobj; // ask for triggers first - if(vpGovObjsTriggersTmp.size()) { - nHashGovobj = vpGovObjsTriggersTmp.back()->GetHash(); + if (vTriggerObjHashes.size()) { + nHashGovobj = vTriggerObjHashes.back(); } else { - if(vpGovObjsTmp.empty()) break; - nHashGovobj = vpGovObjsTmp.back()->GetHash(); + if (vOtherObjHashes.empty()) break; + nHashGovobj = vOtherObjHashes.back(); } bool fAsked = false; for (const auto& pnode : vNodesCopy) { @@ -1132,33 +1110,33 @@ int CGovernanceManager::RequestGovernanceObjectVotes(const std::vector& // they stay connected for a short period of time and it's possible that we won't get everything we should. // Only use outbound connections - inbound connection could be a "masternode" connection // initiated from another node, so skip it too. - if(pnode->fMasternode || (fMasternodeMode && pnode->fInbound)) continue; + if (pnode->fMasternode || (fMasternodeMode && pnode->fInbound)) continue; // only use up to date peers - if(pnode->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) continue; + if (pnode->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) continue; // stop early to prevent setAskFor overflow size_t nProjectedSize = pnode->setAskFor.size() + nProjectedVotes; - if(nProjectedSize > SETASKFOR_MAX_SZ/2) continue; + if (nProjectedSize > SETASKFOR_MAX_SZ / 2) continue; // to early to ask the same node - if(mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; + if (mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; RequestGovernanceObject(pnode, nHashGovobj, connman, true); mapAskedRecently[nHashGovobj][pnode->addr] = nNow + nTimeout; fAsked = true; // stop loop if max number of peers per obj was asked - if(mapAskedRecently[nHashGovobj].size() >= nPeersPerHashMax) break; + if (mapAskedRecently[nHashGovobj].size() >= nPeersPerHashMax) break; } // NOTE: this should match `if` above (the one before `while`) - if(vpGovObjsTriggersTmp.size()) { - vpGovObjsTriggersTmp.pop_back(); + if (vTriggerObjHashes.size()) { + vTriggerObjHashes.pop_back(); } else { - vpGovObjsTmp.pop_back(); + vOtherObjHashes.pop_back(); } - if(!fAsked) i--; + if (!fAsked) i--; } - LogPrint("gobject", "CGovernanceManager::RequestGovernanceObjectVotes -- end: vpGovObjsTriggersTmp %d vpGovObjsTmp %d mapAskedRecently %d\n", - vpGovObjsTriggersTmp.size(), vpGovObjsTmp.size(), mapAskedRecently.size()); + LogPrint("gobject", "CGovernanceManager::RequestGovernanceObjectVotes -- end: vTriggerObjHashes %d vOtherObjHashes %d mapAskedRecently %d\n", + vTriggerObjHashes.size(), vOtherObjHashes.size(), mapAskedRecently.size()); - return int(vpGovObjsTriggersTmp.size() + vpGovObjsTmp.size()); + return int(vTriggerObjHashes.size() + vOtherObjHashes.size()); } bool CGovernanceManager::AcceptObjectMessage(const uint256& nHash) @@ -1176,7 +1154,7 @@ bool CGovernanceManager::AcceptVoteMessage(const uint256& nHash) bool CGovernanceManager::AcceptMessage(const uint256& nHash, hash_s_t& setHash) { hash_s_it it = setHash.find(nHash); - if(it == setHash.end()) { + if (it == setHash.end()) { // We never requested this return false; } @@ -1190,10 +1168,10 @@ void CGovernanceManager::RebuildIndexes() LOCK(cs); cmapVoteToObject.Clear(); - for(object_m_it it = mapObjects.begin(); it != mapObjects.end(); ++it) { - CGovernanceObject& govobj = it->second; + for (auto& objPair : mapObjects) { + CGovernanceObject& govobj = objPair.second; std::vector vecVotes = govobj.GetVoteFile().GetVotes(); - for(size_t i = 0; i < vecVotes.size(); ++i) { + for (size_t i = 0; i < vecVotes.size(); ++i) { cmapVoteToObject.Insert(vecVotes[i].GetHash(), &govobj); } } @@ -1206,7 +1184,7 @@ void CGovernanceManager::AddCachedTriggers() for (auto& objpair : mapObjects) { CGovernanceObject& govobj = objpair.second; - if(govobj.nObjectType != GOVERNANCE_OBJECT_TRIGGER) { + if (govobj.nObjectType != GOVERNANCE_OBJECT_TRIGGER) { continue; } @@ -1238,27 +1216,24 @@ std::string CGovernanceManager::ToString() const int nTriggerCount = 0; int nOtherCount = 0; - object_m_cit it = mapObjects.begin(); - - while(it != mapObjects.end()) { - switch(it->second.GetObjectType()) { - case GOVERNANCE_OBJECT_PROPOSAL: - nProposalCount++; - break; - case GOVERNANCE_OBJECT_TRIGGER: - nTriggerCount++; - break; - default: - nOtherCount++; - break; + for (const auto& objPair : mapObjects) { + switch (objPair.second.GetObjectType()) { + case GOVERNANCE_OBJECT_PROPOSAL: + nProposalCount++; + break; + case GOVERNANCE_OBJECT_TRIGGER: + nTriggerCount++; + break; + default: + nOtherCount++; + break; } - ++it; } return strprintf("Governance Objects: %d (Proposals: %d, Triggers: %d, Other: %d; Erased: %d), Votes: %d", - (int)mapObjects.size(), - nProposalCount, nTriggerCount, nOtherCount, (int)mapErasedGovernanceObjects.size(), - (int)cmapVoteToObject.GetSize()); + (int)mapObjects.size(), + nProposalCount, nTriggerCount, nOtherCount, (int)mapErasedGovernanceObjects.size(), + (int)cmapVoteToObject.GetSize()); } UniValue CGovernanceManager::ToJson() const @@ -1270,16 +1245,16 @@ UniValue CGovernanceManager::ToJson() const int nOtherCount = 0; for (const auto& objpair : mapObjects) { - switch(objpair.second.GetObjectType()) { - case GOVERNANCE_OBJECT_PROPOSAL: - nProposalCount++; - break; - case GOVERNANCE_OBJECT_TRIGGER: - nTriggerCount++; - break; - default: - nOtherCount++; - break; + switch (objpair.second.GetObjectType()) { + case GOVERNANCE_OBJECT_PROPOSAL: + nProposalCount++; + break; + case GOVERNANCE_OBJECT_TRIGGER: + nTriggerCount++; + break; + default: + nOtherCount++; + break; } } @@ -1293,14 +1268,14 @@ UniValue CGovernanceManager::ToJson() const return jsonObj; } -void CGovernanceManager::UpdatedBlockTip(const CBlockIndex *pindex, CConnman& connman) +void CGovernanceManager::UpdatedBlockTip(const CBlockIndex* pindex, CConnman& connman) { // Note this gets called from ActivateBestChain without cs_main being held // so it should be safe to lock our mutex here without risking a deadlock // On the other hand it should be safe for us to access pindex without holding a lock // on cs_main because the CBlockIndex objects are dynamically allocated and // presumably never deleted. - if(!pindex) { + if (!pindex) { return; } @@ -1321,20 +1296,17 @@ void CGovernanceManager::RequestOrphanObjects(CConnman& connman) std::vector vecHashes; LOCK(cs); cmmapOrphanVotes.GetKeys(vecHashes); - for(size_t i = 0; i < vecHashes.size(); ++i) { - const uint256& nHash = vecHashes[i]; - if(mapObjects.find(nHash) == mapObjects.end()) { + for (const uint256& nHash : vecHashes) { + if (mapObjects.find(nHash) == mapObjects.end()) { vecHashesFiltered.push_back(nHash); } } } LogPrint("gobject", "CGovernanceObject::RequestOrphanObjects -- number objects = %d\n", vecHashesFiltered.size()); - for(size_t i = 0; i < vecHashesFiltered.size(); ++i) { - const uint256& nHash = vecHashesFiltered[i]; - for(size_t j = 0; j < vNodesCopy.size(); ++j) { - CNode* pnode = vNodesCopy[j]; - if(pnode->fMasternode) { + for (const uint256& nHash : vecHashesFiltered) { + for (CNode* pnode : vNodesCopy) { + if (pnode->fMasternode) { continue; } RequestGovernanceObject(pnode, nHash, connman); @@ -1352,11 +1324,11 @@ void CGovernanceManager::CleanOrphanObjects() int64_t nNow = GetAdjustedTime(); vote_cmm_t::list_cit it = items.begin(); - while(it != items.end()) { + while (it != items.end()) { vote_cmm_t::list_cit prevIt = it; ++it; const vote_time_pair_t& pairVote = prevIt->value; - if(pairVote.second < nNow) { + if (pairVote.second < nNow) { cmmapOrphanVotes.Erase(prevIt->key, prevIt->value); } } diff --git a/src/governance.h b/src/governance.h index 2b5363335ced..aa76a36ae382 100644 --- a/src/governance.h +++ b/src/governance.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -30,7 +30,8 @@ class CGovernanceVote; extern CGovernanceManager governance; struct ExpirationInfo { - ExpirationInfo(int64_t _nExpirationTime, int _idFrom) : nExpirationTime(_nExpirationTime), idFrom(_idFrom) {} + ExpirationInfo(int64_t _nExpirationTime, int _idFrom) : + nExpirationTime(_nExpirationTime), idFrom(_idFrom) {} int64_t nExpirationTime; NodeId idFrom; @@ -52,16 +53,17 @@ class CRateCheckBuffer bool fBufferEmpty; public: - CRateCheckBuffer() - : vecTimestamps(RATE_BUFFER_SIZE), - nDataStart(0), - nDataEnd(0), - fBufferEmpty(true) - {} + CRateCheckBuffer() : + vecTimestamps(RATE_BUFFER_SIZE), + nDataStart(0), + nDataEnd(0), + fBufferEmpty(true) + { + } void AddTimestamp(int64_t nTimestamp) { - if((nDataEnd == nDataStart) && !fBufferEmpty) { + if ((nDataEnd == nDataStart) && !fBufferEmpty) { // Buffer full, discard 1st element nDataStart = (nDataStart + 1) % RATE_BUFFER_SIZE; } @@ -74,15 +76,15 @@ class CRateCheckBuffer { int nIndex = nDataStart; int64_t nMin = std::numeric_limits::max(); - if(fBufferEmpty) { + if (fBufferEmpty) { return nMin; } do { - if(vecTimestamps[nIndex] < nMin) { + if (vecTimestamps[nIndex] < nMin) { nMin = vecTimestamps[nIndex]; } nIndex = (nIndex + 1) % RATE_BUFFER_SIZE; - } while(nIndex != nDataEnd); + } while (nIndex != nDataEnd); return nMin; } @@ -90,28 +92,27 @@ class CRateCheckBuffer { int nIndex = nDataStart; int64_t nMax = 0; - if(fBufferEmpty) { + if (fBufferEmpty) { return nMax; } do { - if(vecTimestamps[nIndex] > nMax) { + if (vecTimestamps[nIndex] > nMax) { nMax = vecTimestamps[nIndex]; } nIndex = (nIndex + 1) % RATE_BUFFER_SIZE; - } while(nIndex != nDataEnd); + } while (nIndex != nDataEnd); return nMax; } int GetCount() { int nCount = 0; - if(fBufferEmpty) { + if (fBufferEmpty) { return 0; } - if(nDataEnd > nDataStart) { + if (nDataEnd > nDataStart) { nCount = nDataEnd - nDataStart; - } - else { + } else { nCount = RATE_BUFFER_SIZE - nDataStart + nDataEnd; } @@ -121,12 +122,12 @@ class CRateCheckBuffer double GetRate() { int nCount = GetCount(); - if(nCount < RATE_BUFFER_SIZE) { + if (nCount < RATE_BUFFER_SIZE) { return 0.0; } int64_t nMin = GetMinTimestamp(); int64_t nMax = GetMaxTimestamp(); - if(nMin == nMax) { + if (nMin == nMax) { // multiple objects with the same timestamp => infinite rate return 1.0e10; } @@ -154,10 +155,11 @@ class CGovernanceManager public: // Types struct last_object_rec { - last_object_rec(bool fStatusOKIn = true) - : triggerBuffer(), - fStatusOK(fStatusOKIn) - {} + last_object_rec(bool fStatusOKIn = true) : + triggerBuffer(), + fStatusOK(fStatusOKIn) + { + } ADD_SERIALIZE_METHODS; @@ -193,7 +195,7 @@ class CGovernanceManager typedef object_m_t::size_type size_type; - typedef std::map txout_m_t; + typedef std::map txout_m_t; typedef txout_m_t::iterator txout_m_it; @@ -266,7 +268,8 @@ class CGovernanceManager bool fPrevValue; public: - ScopedLockBool(CCriticalSection& _cs, bool& _ref, bool _value) : ref(_ref) + ScopedLockBool(CCriticalSection& _cs, bool& _ref, bool _value) : + ref(_ref) { AssertLockHeld(_cs); fPrevValue = ref; @@ -308,11 +311,11 @@ class CGovernanceManager std::vector GetCurrentVotes(const uint256& nParentHash, const COutPoint& mnCollateralOutpointFilter) const; std::vector GetAllNewerThan(int64_t nMoreThanTime) const; - void AddGovernanceObject(CGovernanceObject& govobj, CConnman& connman, CNode* pfrom = NULL); + void AddGovernanceObject(CGovernanceObject& govobj, CConnman& connman, CNode* pfrom = nullptr); void UpdateCachesAndClean(); - void CheckAndRemove() {UpdateCachesAndClean();} + void CheckAndRemove() { UpdateCachesAndClean(); } void Clear() { @@ -333,13 +336,13 @@ class CGovernanceManager ADD_SERIALIZE_METHODS; template - inline void SerializationOp(Stream& s, Operation ser_action) { + inline void SerializationOp(Stream& s, Operation ser_action) + { LOCK(cs); std::string strVersion; - if(ser_action.ForRead()) { + if (ser_action.ForRead()) { READWRITE(strVersion); - } - else { + } else { strVersion = SERIALIZATION_VERSION_STRING; READWRITE(strVersion); } @@ -349,13 +352,13 @@ class CGovernanceManager READWRITE(cmmapOrphanVotes); READWRITE(mapObjects); READWRITE(mapLastMasternodeObject); - if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { + if (ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { Clear(); return; } } - void UpdatedBlockTip(const CBlockIndex *pindex, CConnman& connman); + void UpdatedBlockTip(const CBlockIndex* pindex, CConnman& connman); int64_t GetLastDiffTime() const { return nTimeLastDiff; } void UpdateLastDiffTime(int64_t nTimeIn) { nTimeLastDiff = nTimeIn; } @@ -388,9 +391,10 @@ class CGovernanceManager bool MasternodeRateCheck(const CGovernanceObject& govobj, bool fUpdateFailStatus, bool fForce, bool& fRateCheckBypassed); - bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) { - bool fOK = ProcessVote(NULL, vote, exception, connman); - if(fOK) { + bool ProcessVoteAndRelay(const CGovernanceVote& vote, CGovernanceException& exception, CConnman& connman) + { + bool fOK = ProcessVote(nullptr, vote, exception, connman); + if (fOK) { vote.Relay(connman); } return fOK; @@ -402,7 +406,8 @@ class CGovernanceManager void CheckPostponedObjects(CConnman& connman); - bool AreRateChecksEnabled() const { + bool AreRateChecksEnabled() const + { LOCK(cs); return fRateChecksEnabled; } @@ -444,7 +449,6 @@ class CGovernanceManager void RequestOrphanObjects(CConnman& connman); void CleanOrphanObjects(); - }; #endif diff --git a/src/hash.h b/src/hash.h index 9c5831da9735..643c18bbc26c 100644 --- a/src/hash.h +++ b/src/hash.h @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,17 +15,17 @@ #include "uint256.h" #include "version.h" -#include "crypto/sph_blake.h" -#include "crypto/sph_bmw.h" -#include "crypto/sph_groestl.h" -#include "crypto/sph_jh.h" -#include "crypto/sph_keccak.h" -#include "crypto/sph_skein.h" -#include "crypto/sph_luffa.h" -#include "crypto/sph_cubehash.h" -#include "crypto/sph_shavite.h" -#include "crypto/sph_simd.h" -#include "crypto/sph_echo.h" +#include "crypto/Lyra2RE/sph_blake.h" +#include "crypto/Lyra2RE/sph_bmw.h" +#include "crypto/Lyra2RE/sph_groestl.h" +#include "crypto/Lyra2RE/sph_jh.h" +#include "crypto/Lyra2RE/sph_keccak.h" +#include "crypto/Lyra2RE/sph_skein.h" +#include "crypto/Lyra2RE/sph_luffa.h" +#include "crypto/Lyra2RE/sph_cubehash.h" +#include "crypto/Lyra2RE/sph_shavite.h" +#include "crypto/Lyra2RE/sph_simd.h" +#include "crypto/Lyra2RE/sph_echo.h" #include @@ -312,71 +312,4 @@ class CSipHasher uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val); uint64_t SipHashUint256Extra(uint64_t k0, uint64_t k1, const uint256& val, uint32_t extra); -/* ----------- Absolute Hash ------------------------------------------------ */ -template -inline uint256 HashX11(const T1 pbegin, const T1 pend) - -{ - sph_blake512_context ctx_blake; - sph_bmw512_context ctx_bmw; - sph_groestl512_context ctx_groestl; - sph_jh512_context ctx_jh; - sph_keccak512_context ctx_keccak; - sph_skein512_context ctx_skein; - sph_luffa512_context ctx_luffa; - sph_cubehash512_context ctx_cubehash; - sph_shavite512_context ctx_shavite; - sph_simd512_context ctx_simd; - sph_echo512_context ctx_echo; - static unsigned char pblank[1]; - - uint512 hash[11]; - - sph_blake512_init(&ctx_blake); - sph_blake512 (&ctx_blake, (pbegin == pend ? pblank : static_cast(&pbegin[0])), (pend - pbegin) * sizeof(pbegin[0])); - sph_blake512_close(&ctx_blake, static_cast(&hash[0])); - - sph_bmw512_init(&ctx_bmw); - sph_bmw512 (&ctx_bmw, static_cast(&hash[0]), 64); - sph_bmw512_close(&ctx_bmw, static_cast(&hash[1])); - - sph_groestl512_init(&ctx_groestl); - sph_groestl512 (&ctx_groestl, static_cast(&hash[1]), 64); - sph_groestl512_close(&ctx_groestl, static_cast(&hash[2])); - - sph_skein512_init(&ctx_skein); - sph_skein512 (&ctx_skein, static_cast(&hash[2]), 64); - sph_skein512_close(&ctx_skein, static_cast(&hash[3])); - - sph_jh512_init(&ctx_jh); - sph_jh512 (&ctx_jh, static_cast(&hash[3]), 64); - sph_jh512_close(&ctx_jh, static_cast(&hash[4])); - - sph_keccak512_init(&ctx_keccak); - sph_keccak512 (&ctx_keccak, static_cast(&hash[4]), 64); - sph_keccak512_close(&ctx_keccak, static_cast(&hash[5])); - - sph_luffa512_init(&ctx_luffa); - sph_luffa512 (&ctx_luffa, static_cast(&hash[5]), 64); - sph_luffa512_close(&ctx_luffa, static_cast(&hash[6])); - - sph_cubehash512_init(&ctx_cubehash); - sph_cubehash512 (&ctx_cubehash, static_cast(&hash[6]), 64); - sph_cubehash512_close(&ctx_cubehash, static_cast(&hash[7])); - - sph_shavite512_init(&ctx_shavite); - sph_shavite512(&ctx_shavite, static_cast(&hash[7]), 64); - sph_shavite512_close(&ctx_shavite, static_cast(&hash[8])); - - sph_simd512_init(&ctx_simd); - sph_simd512 (&ctx_simd, static_cast(&hash[8]), 64); - sph_simd512_close(&ctx_simd, static_cast(&hash[9])); - - sph_echo512_init(&ctx_echo); - sph_echo512 (&ctx_echo, static_cast(&hash[9]), 64); - sph_echo512_close(&ctx_echo, static_cast(&hash[10])); - - return hash[10].trim256(); -} - #endif // BITCOIN_HASH_H diff --git a/src/hdchain.cpp b/src/hdchain.cpp index ce426e9e7151..429c7e8888ba 100644 --- a/src/hdchain.cpp +++ b/src/hdchain.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying #include "base58.h" @@ -170,9 +170,9 @@ void CHDChain::DeriveChildExtKey(uint32_t nAccountIndex, bool fInternal, uint32_ purposeKey.Derive(cointypeKey, Params().ExtCoinType() | 0x80000000); // derive m/purpose'/coin_type'/account' cointypeKey.Derive(accountKey, nAccountIndex | 0x80000000); - // derive m/purpose'/coin_type'/account/change + // derive m/purpose'/coin_type'/account'/change accountKey.Derive(changeKey, fInternal ? 1 : 0); - // derive m/purpose'/coin_type'/account/change/address_index + // derive m/purpose'/coin_type'/account'/change/address_index changeKey.Derive(extKeyRet, nChildIndex); } diff --git a/src/hdchain.h b/src/hdchain.h index dd25b413b27a..74a89142c562 100644 --- a/src/hdchain.h +++ b/src/hdchain.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying #ifndef ABSOLUTE_HDCHAIN_H #define ABSOLUTE_HDCHAIN_H diff --git a/src/immer/algorithm.hpp b/src/immer/algorithm.hpp new file mode 100644 index 000000000000..df9ff28a8314 --- /dev/null +++ b/src/immer/algorithm.hpp @@ -0,0 +1,214 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +namespace immer { + +/** + * @defgroup algorithm + * @{ + */ + +/*@{*/ +// Right now these algorithms dispatch directly to the vector +// implementations unconditionally. This will be changed in the +// future to support other kinds of containers. + +/*! + * Apply operation `fn` for every contiguous *chunk* of data in the + * range sequentially. Each time, `Fn` is passed two `value_type` + * pointers describing a range over a part of the vector. This allows + * iterating over the elements in the most efficient way. + * + * @rst + * + * .. tip:: This is a low level method. Most of the time, :doc:`other + * wrapper algorithms ` should be used instead. + * + * @endrst + */ +template +void for_each_chunk(const Range& r, Fn&& fn) +{ + r.impl().for_each_chunk(std::forward(fn)); +} + +template +void for_each_chunk(const Iterator& first, const Iterator& last, Fn&& fn) +{ + assert(&first.impl() == &last.impl()); + first.impl().for_each_chunk(first.index(), last.index(), + std::forward(fn)); +} + +template +void for_each_chunk(const T* first, const T* last, Fn&& fn) +{ + std::forward(fn)(first, last); +} + +/*! + * Apply operation `fn` for every contiguous *chunk* of data in the + * range sequentially, until `fn` returns `false`. Each time, `Fn` is + * passed two `value_type` pointers describing a range over a part of + * the vector. This allows iterating over the elements in the most + * efficient way. + * + * @rst + * + * .. tip:: This is a low level method. Most of the time, :doc:`other + * wrapper algorithms ` should be used instead. + * + * @endrst + */ +template +bool for_each_chunk_p(const Range& r, Fn&& fn) +{ + return r.impl().for_each_chunk_p(std::forward(fn)); +} + +template +bool for_each_chunk_p(const Iterator& first, const Iterator& last, Fn&& fn) +{ + assert(&first.impl() == &last.impl()); + return first.impl().for_each_chunk_p(first.index(), last.index(), + std::forward(fn)); +} + +template +bool for_each_chunk_p(const T* first, const T* last, Fn&& fn) +{ + return std::forward(fn)(first, last); +} + +/*! + * Equivalent of `std::accumulate` applied to the range `r`. + */ +template +T accumulate(Range&& r, T init) +{ + for_each_chunk(r, [&] (auto first, auto last) { + init = std::accumulate(first, last, init); + }); + return init; +} + +template +T accumulate(Range&& r, T init, Fn fn) +{ + for_each_chunk(r, [&] (auto first, auto last) { + init = std::accumulate(first, last, init, fn); + }); + return init; +} + +/*! + * Equivalent of `std::accumulate` applied to the range @f$ [first, + * last) @f$. + */ +template +T accumulate(Iterator first, Iterator last, T init) +{ + for_each_chunk(first, last, [&] (auto first, auto last) { + init = std::accumulate(first, last, init); + }); + return init; +} + +template +T accumulate(Iterator first, Iterator last, T init, Fn fn) +{ + for_each_chunk(first, last, [&] (auto first, auto last) { + init = std::accumulate(first, last, init, fn); + }); + return init; +} + +/*! + * Equivalent of `std::for_each` applied to the range `r`. + */ +template +Fn&& for_each(Range&& r, Fn&& fn) +{ + for_each_chunk(r, [&] (auto first, auto last) { + for (; first != last; ++first) + fn(*first); + }); + return std::forward(fn); +} + +/*! + * Equivalent of `std::for_each` applied to the range @f$ [first, + * last) @f$. + */ +template +Fn&& for_each(Iterator first, Iterator last, Fn&& fn) +{ + for_each_chunk(first, last, [&] (auto first, auto last) { + for (; first != last; ++first) + fn(*first); + }); + return std::forward(fn); +} + +/*! + * Equivalent of `std::copy` applied to the range `r`. + */ +template +OutIter copy(Range&& r, OutIter out) +{ + for_each_chunk(r, [&] (auto first, auto last) { + out = std::copy(first, last, out); + }); + return out; +} + +/*! + * Equivalent of `std::copy` applied to the range @f$ [first, + * last) @f$. + */ +template +OutIter copy(InIter first, InIter last, OutIter out) +{ + for_each_chunk(first, last, [&] (auto first, auto last) { + out = std::copy(first, last, out); + }); + return out; +} + +/*! + * Equivalent of `std::all_of` applied to the range `r`. + */ +template +bool all_of(Range&& r, Pred p) +{ + return for_each_chunk_p(r, [&] (auto first, auto last) { + return std::all_of(first, last, p); + }); +} + +/*! + * Equivalent of `std::all_of` applied to the range @f$ [first, last) + * @f$. + */ +template +bool all_of(Iter first, Iter last, Pred p) +{ + return for_each_chunk_p(first, last, [&] (auto first, auto last) { + return std::all_of(first, last, p); + }); +} + +/** @} */ // group: algorithm + +} // namespace immer diff --git a/src/immer/array.hpp b/src/immer/array.hpp new file mode 100644 index 000000000000..08a8b0916751 --- /dev/null +++ b/src/immer/array.hpp @@ -0,0 +1,311 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { + +template +class array_transient; + +/*! + * Immutable container that stores a sequence of elements in + * contiguous memory. + * + * @tparam T The type of the values to be stored in the container. + * + * @rst + * + * It supports the most efficient iteration and random access, + * equivalent to a ``std::vector`` or ``std::array``, but all + * manipulations are :math:`O(size)`. + * + * .. tip:: Don't be fooled by the bad complexity of this data + * structure. It is a great choice for short sequence or when it + * is seldom or never changed. This depends on the ``sizeof(T)`` + * and the expensiveness of its ``T``'s copy constructor, in case + * of doubt, measure. For basic types, using an `array` when + * :math:`n < 100` is a good heuristic. + * + * .. warning:: The current implementation depends on + * ``boost::intrusive_ptr`` and does not support :doc:`memory + * policies`. This will be fixed soon. + * + * @endrst + */ +template +class array +{ + using impl_t = std::conditional_t< + MemoryPolicy::use_transient_rvalues, + detail::arrays::with_capacity, + detail::arrays::no_capacity>; + + using move_t = + std::integral_constant; + +public: + using value_type = T; + using reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using const_reference = const T&; + + using iterator = const T*; + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + + using memory_policy = MemoryPolicy; + using transient_type = array_transient; + + /*! + * Default constructor. It creates an array of `size() == 0`. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + array() = default; + + /*! + * Constructs a vector containing the elements in `values`. + */ + array(std::initializer_list values) + : impl_{impl_t::from_initializer_list(values)} + {} + + /*! + * Constructs a vector containing the elements in the range + * defined by the input iterators `first` and `last`. + */ + template + array(Iter first, Iter last) + : impl_{impl_t::from_range(first, last)} + {} + + /*! + * Constructs a vector containing the element `val` repeated `n` + * times. + */ + array(size_type n, T v = {}) + : impl_{impl_t::from_fill(n, v)} + {} + + /*! + * Returns an iterator pointing at the first element of the + * collection. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + iterator begin() const { return impl_.data(); } + + /*! + * Returns an iterator pointing just after the last element of the + * collection. It does not allocate and its complexity is @f$ O(1) @f$. + */ + iterator end() const { return impl_.data() + impl_.size; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing at the first element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing after the last element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rend() const { return reverse_iterator{begin()}; } + + /*! + * Returns the number of elements in the container. It does + * not allocate memory and its complexity is @f$ O(1) @f$. + */ + std::size_t size() const { return impl_.size; } + + /*! + * Returns `true` if there are no elements in the container. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + bool empty() const { return impl_.d->empty(); } + + /*! + * Access the raw data. + */ + const T* data() const { return impl_.data(); } + + /*! + * Access the last element. + */ + const T& back() const { return data()[size() - 1]; } + + /*! + * Access the first element. + */ + const T& front() const { return data()[0]; } + + /*! + * Returns a `const` reference to the element at position `index`. + * It is undefined when @f$ 0 index \geq size() @f$. It does not + * allocate memory and its complexity is *effectively* @f$ O(1) + * @f$. + */ + reference operator[] (size_type index) const + { return impl_.get(index); } + + /*! + * Returns a `const` reference to the element at position + * `index`. It throws an `std::out_of_range` exception when @f$ + * index \geq size() @f$. It does not allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + reference at(size_type index) const + { return impl_.get_check(index); } + + /*! + * Returns whether the vectors are equal. + */ + bool operator==(const array& other) const + { return impl_.equals(other.impl_); } + bool operator!=(const array& other) const + { return !(*this == other); } + + /*! + * Returns an array with `value` inserted at the end. It may + * allocate memory and its complexity is @f$ O(size) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/array/array.cpp + * :language: c++ + * :dedent: 8 + * :start-after: push-back/start + * :end-before: push-back/end + * + * @endrst + */ + array push_back(value_type value) const& + { return impl_.push_back(std::move(value)); } + + decltype(auto) push_back(value_type value) && + { return push_back_move(move_t{}, std::move(value)); } + + /*! + * Returns an array containing value `value` at position `idx`. + * Undefined for `index >= size()`. + * It may allocate memory and its complexity is @f$ O(size) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/array/array.cpp + * :language: c++ + * :dedent: 8 + * :start-after: set/start + * :end-before: set/end + * + * @endrst + */ + array set(std::size_t index, value_type value) const& + { return impl_.assoc(index, std::move(value)); } + + decltype(auto) set(size_type index, value_type value) && + { return set_move(move_t{}, index, std::move(value)); } + + /*! + * Returns an array containing the result of the expression + * `fn((*this)[idx])` at position `idx`. + * Undefined for `index >= size()`. + * It may allocate memory and its complexity is @f$ O(size) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/array/array.cpp + * :language: c++ + * :dedent: 8 + * :start-after: update/start + * :end-before: update/end + * + * @endrst + */ + template + array update(std::size_t index, FnT&& fn) const& + { return impl_.update(index, std::forward(fn)); } + + template + decltype(auto) update(size_type index, FnT&& fn) && + { return update_move(move_t{}, index, std::forward(fn)); } + + /*! + * Returns a array containing only the first `min(elems, size())` + * elements. It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/array/array.cpp + * :language: c++ + * :dedent: 8 + * :start-after: take/start + * :end-before: take/end + * + * @endrst + */ + array take(size_type elems) const& + { return impl_.take(elems); } + + decltype(auto) take(size_type elems) && + { return take_move(move_t{}, elems); } + + /*! + * Returns an @a transient form of this container, an + * `immer::array_transient`. + */ + transient_type transient() const& + { return transient_type{ impl_ }; } + transient_type transient() && + { return transient_type{ std::move(impl_) }; } + + // Semi-private + const impl_t& impl() const { return impl_; } + +private: + friend transient_type; + + array(impl_t impl) : impl_(std::move(impl)) {} + + array&& push_back_move(std::true_type, value_type value) + { impl_.push_back_mut({}, std::move(value)); return std::move(*this); } + array push_back_move(std::false_type, value_type value) + { return impl_.push_back(std::move(value)); } + + array&& set_move(std::true_type, size_type index, value_type value) + { impl_.assoc_mut({}, index, std::move(value)); return std::move(*this); } + array set_move(std::false_type, size_type index, value_type value) + { return impl_.assoc(index, std::move(value)); } + + template + array&& update_move(std::true_type, size_type index, Fn&& fn) + { impl_.update_mut({}, index, std::forward(fn)); return std::move(*this); } + template + array update_move(std::false_type, size_type index, Fn&& fn) + { return impl_.update(index, std::forward(fn)); } + + array&& take_move(std::true_type, size_type elems) + { impl_.take_mut({}, elems); return std::move(*this); } + array take_move(std::false_type, size_type elems) + { return impl_.take(elems); } + + impl_t impl_ = impl_t::empty; +}; + +} /* namespace immer */ diff --git a/src/immer/array_transient.hpp b/src/immer/array_transient.hpp new file mode 100644 index 000000000000..0084e47dddc8 --- /dev/null +++ b/src/immer/array_transient.hpp @@ -0,0 +1,187 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { + +template +class array; + +/*! + * Mutable version of `immer::array`. + * + * @rst + * + * Refer to :doc:`transients` to learn more about when and how to use + * the mutable versions of immutable containers. + * + * @endrst + */ +template +class array_transient + : MemoryPolicy::transience_t::owner +{ + using impl_t = detail::arrays::with_capacity; + using impl_no_capacity_t = detail::arrays::no_capacity; + using owner_t = typename MemoryPolicy::transience_t::owner; + +public: + using value_type = T; + using reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using const_reference = const T&; + + using iterator = const T*; + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + + using memory_policy = MemoryPolicy; + using persistent_type = array; + + /*! + * Default constructor. It creates a mutable array of `size() == + * 0`. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + array_transient() = default; + + /*! + * Returns an iterator pointing at the first element of the + * collection. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + iterator begin() const { return impl_.data(); } + + /*! + * Returns an iterator pointing just after the last element of the + * collection. It does not allocate and its complexity is @f$ O(1) @f$. + */ + iterator end() const { return impl_.data() + impl_.size; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing at the first element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing after the last element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rend() const { return reverse_iterator{begin()}; } + + /*! + * Returns the number of elements in the container. It does + * not allocate memory and its complexity is @f$ O(1) @f$. + */ + std::size_t size() const { return impl_.size; } + + /*! + * Returns `true` if there are no elements in the container. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + bool empty() const { return impl_.d->empty(); } + + /*! + * Access the raw data. + */ + const T* data() const { return impl_.data(); } + + /*! + * Access the last element. + */ + const T& back() const { return data()[size() - 1]; } + + /*! + * Access the first element. + */ + const T& front() const { return data()[0]; } + + /*! + * Returns a `const` reference to the element at position `index`. + * It is undefined when @f$ 0 index \geq size() @f$. It does not + * allocate memory and its complexity is *effectively* @f$ O(1) + * @f$. + */ + reference operator[] (size_type index) const + { return impl_.get(index); } + + /*! + * Returns a `const` reference to the element at position + * `index`. It throws an `std::out_of_range` exception when @f$ + * index \geq size() @f$. It does not allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + reference at(size_type index) const + { return impl_.get_check(index); } + + /*! + * Inserts `value` at the end. It may allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + void push_back(value_type value) + { impl_.push_back_mut(*this, std::move(value)); } + + /*! + * Sets to the value `value` at position `idx`. + * Undefined for `index >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + void set(size_type index, value_type value) + { impl_.assoc_mut(*this, index, std::move(value)); } + + /*! + * Updates the array to contain the result of the expression + * `fn((*this)[idx])` at position `idx`. + * Undefined for `0 >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + template + void update(size_type index, FnT&& fn) + { impl_.update_mut(*this, index, std::forward(fn)); } + + /*! + * Resizes the array to only contain the first `min(elems, size())` + * elements. It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + void take(size_type elems) + { impl_.take_mut(*this, elems); } + + /*! + * Returns an @a immutable form of this container, an + * `immer::array`. + */ + persistent_type persistent() & + { + this->owner_t::operator=(owner_t{}); + return persistent_type{ impl_ }; + } + persistent_type persistent() && + { return persistent_type{ std::move(impl_) }; } + +private: + friend persistent_type; + + array_transient(impl_t impl) + : impl_(std::move(impl)) + {} + + impl_t impl_ = impl_t::empty; +}; + +} // namespace immer diff --git a/src/immer/box.hpp b/src/immer/box.hpp new file mode 100644 index 000000000000..2d9f159db474 --- /dev/null +++ b/src/immer/box.hpp @@ -0,0 +1,159 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { + +/*! + * Immutable box for a single value of type `T`. + * + * The box is always copiable and movable. The `T` copy or move + * operations are never called. Since a box is immutable, copying or + * moving just copy the underlying pointers. + */ +template +class box +{ + struct holder : MemoryPolicy::refcount + { + T value; + + template + holder(Args&&... args) : value{std::forward(args)...} {} + }; + + using heap = typename MemoryPolicy::heap::type; + + holder* impl_ = nullptr; + + box(holder* impl) : impl_{impl} {} + +public: + using value_type = T; + using memory_policy = MemoryPolicy; + + /*! + * Constructs a box holding `T{}`. + */ + box() : impl_{detail::make()} {} + + /*! + * Constructs a box holding `T{arg}` + */ + template >::value && + std::is_constructible::value>> + box(Arg&& arg) + : impl_{detail::make(std::forward(arg))} {} + + /*! + * Constructs a box holding `T{arg1, arg2, args...}` + */ + template + box(Arg1&& arg1, Arg2&& arg2, Args&& ...args) + : impl_{detail::make( + std::forward(arg1), + std::forward(arg2), + std::forward(args)...)} + {} + + friend void swap(box& a, box& b) + { using std::swap; swap(a.impl_, b.impl_); } + + box(box&& other) { swap(*this, other); } + box(const box& other) : impl_(other.impl_) { impl_->inc(); } + box& operator=(box&& other) { swap(*this, other); return *this; } + box& operator=(const box& other) + { + auto aux = other; + swap(*this, aux); + return *this; + } + ~box() + { + if (impl_ && impl_->dec()) { + impl_->~holder(); + heap::deallocate(sizeof(holder), impl_); + } + } + + /*! Query the current value. */ + const T& get() const { return impl_->value; } + + /*! Conversion to the boxed type. */ + operator const T&() const { return get(); } + + /*! Access via dereference */ + const T& operator* () const { return get(); } + + /*! Access via pointer member access */ + const T* operator-> () const { return &get(); } + + /*! Comparison. */ + bool operator==(detail::exact_t other) const + { return impl_ == other.value.impl_ || get() == other.value.get(); } + // Note that the `exact_t` disambiguates comparisons against `T{}` + // directly. In that case we want to use `operator T&` and + // compare directly. We definitely never want to convert a value + // to a box (which causes an allocation) just to compare it. + bool operator!=(detail::exact_t other) const + { return !(*this == other.value); } + bool operator<(detail::exact_t other) const + { return get() < other.value.get(); } + + /*! + * Returns a new box built by applying the `fn` to the underlying + * value. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/box/box.cpp + * :language: c++ + * :dedent: 8 + * :start-after: update/start + * :end-before: update/end + * + * @endrst + */ + template + box update(Fn&& fn) const& + { + return std::forward(fn)(get()); + } + template + box&& update(Fn&& fn) && + { + if (impl_->unique()) + impl_->value = std::forward(fn)(std::move(impl_->value)); + else + *this = std::forward(fn)(impl_->value); + return std::move(*this); + } +}; + +} // namespace immer + +namespace std { + +template +struct hash> +{ + std::size_t operator() (const immer::box& x) const + { + return std::hash{}(*x); + } +}; + +} // namespace std diff --git a/src/immer/config.hpp b/src/immer/config.hpp new file mode 100644 index 000000000000..f3f19f1ad584 --- /dev/null +++ b/src/immer/config.hpp @@ -0,0 +1,58 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#ifndef IMMER_DEBUG_TRACES +#define IMMER_DEBUG_TRACES 0 +#endif + +#ifndef IMMER_DEBUG_PRINT +#define IMMER_DEBUG_PRINT 0 +#endif + +#ifndef IMMER_DEBUG_DEEP_CHECK +#define IMMER_DEBUG_DEEP_CHECK 0 +#endif + +#if IMMER_DEBUG_TRACES || IMMER_DEBUG_PRINT +#include +#include +#endif + +#if IMMER_DEBUG_TRACES +#define IMMER_TRACE(...) std::cout << __VA_ARGS__ << std::endl +#else +#define IMMER_TRACE(...) +#endif +#define IMMER_TRACE_F(...) \ + IMMER_TRACE(__FILE__ << ":" << __LINE__ << ": " << __VA_ARGS__) +#define IMMER_TRACE_E(expr) \ + IMMER_TRACE(" " << #expr << " = " << (expr)) + +#define IMMER_UNREACHABLE __builtin_unreachable() +#define IMMER_LIKELY(cond) __builtin_expect(!!(cond), 1) +#define IMMER_UNLIKELY(cond) __builtin_expect(!!(cond), 0) +// #define IMMER_PREFETCH(p) __builtin_prefetch(p) +#define IMMER_PREFETCH(p) +#define IMMER_FORCEINLINE inline __attribute__ ((always_inline)) + +#define IMMER_DESCENT_DEEP 0 + +#ifdef NDEBUG +#define IMMER_ENABLE_DEBUG_SIZE_HEAP 0 +#else +#define IMMER_ENABLE_DEBUG_SIZE_HEAP 1 +#endif + +namespace immer { + +const auto default_bits = 5; +const auto default_free_list_size = 1 << 10; + +} // namespace immer diff --git a/src/immer/detail/arrays/no_capacity.hpp b/src/immer/detail/arrays/no_capacity.hpp new file mode 100644 index 000000000000..e056739c35b3 --- /dev/null +++ b/src/immer/detail/arrays/no_capacity.hpp @@ -0,0 +1,192 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { +namespace detail { +namespace arrays { + +template +struct no_capacity +{ + using node_t = node; + using edit_t = typename MemoryPolicy::transience_t::edit; + using size_t = std::size_t; + + node_t* ptr; + size_t size; + + static const no_capacity empty; + + no_capacity(node_t* p, size_t s) + : ptr{p}, size{s} + {} + + no_capacity(const no_capacity& other) + : no_capacity{other.ptr, other.size} + { + inc(); + } + + no_capacity(no_capacity&& other) + : no_capacity{empty} + { + swap(*this, other); + } + + no_capacity& operator=(const no_capacity& other) + { + auto next = other; + swap(*this, next); + return *this; + } + + no_capacity& operator=(no_capacity&& other) + { + swap(*this, other); + return *this; + } + + friend void swap(no_capacity& x, no_capacity& y) + { + using std::swap; + swap(x.ptr, y.ptr); + swap(x.size, y.size); + } + + ~no_capacity() + { + dec(); + } + + void inc() + { + using immer::detail::get; + ptr->refs().inc(); + } + + void dec() + { + using immer::detail::get; + if (ptr->refs().dec()) + node_t::delete_n(ptr, size, size); + } + + T* data() { return ptr->data(); } + const T* data() const { return ptr->data(); } + + template + static no_capacity from_range(Iter first, Iter last) + { + auto count = static_cast(std::distance(first, last)); + return { + node_t::copy_n(count, first, last), + count, + }; + } + + static no_capacity from_fill(size_t n, T v) + { + return { node_t::fill_n(n, v), n }; + } + + template + static no_capacity from_initializer_list(std::initializer_list values) + { + using namespace std; + return from_range(begin(values), end(values)); + } + + template + void for_each_chunk(Fn&& fn) const + { + std::forward(fn)(data(), data() + size); + } + + template + bool for_each_chunk_p(Fn&& fn) const + { + return std::forward(fn)(data(), data() + size); + } + + const T& get(std::size_t index) const + { + return data()[index]; + } + + const T& get_check(std::size_t index) const + { + if (index >= size) + throw std::out_of_range{"out of range"}; + return data()[index]; + } + + bool equals(const no_capacity& other) const + { + return ptr == other.ptr || + (size == other.size && + std::equal(data(), data() + size, other.data())); + } + + no_capacity push_back(T value) const + { + auto p = node_t::copy_n(size + 1, ptr, size); + try { + new (p->data() + size) T{std::move(value)}; + return { p, size + 1 }; + } catch (...) { + node_t::delete_n(p, size, size + 1); + throw; + } + } + + no_capacity assoc(std::size_t idx, T value) const + { + auto p = node_t::copy_n(size, ptr, size); + try { + p->data()[idx] = std::move(value); + return { p, size }; + } catch (...) { + node_t::delete_n(p, size, size); + throw; + } + } + + template + no_capacity update(std::size_t idx, Fn&& op) const + { + auto p = node_t::copy_n(size, ptr, size); + try { + auto& elem = p->data()[idx]; + elem = std::forward(op)(std::move(elem)); + return { p, size }; + } catch (...) { + node_t::delete_n(p, size, size); + throw; + } + } + + no_capacity take(std::size_t sz) const + { + auto p = node_t::copy_n(sz, ptr, sz); + return { p, sz }; + } +}; + +template +const no_capacity no_capacity::empty = { + node_t::make_n(0), + 0, +}; + +} // namespace arrays +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/arrays/node.hpp b/src/immer/detail/arrays/node.hpp new file mode 100644 index 000000000000..5721f5779d30 --- /dev/null +++ b/src/immer/detail/arrays/node.hpp @@ -0,0 +1,129 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +#include + +namespace immer { +namespace detail { +namespace arrays { + +template +struct node +{ + using memory = MemoryPolicy; + using heap = typename MemoryPolicy::heap::type; + using transience = typename memory::transience_t; + using refs_t = typename memory::refcount; + using ownee_t = typename transience::ownee; + using node_t = node; + using edit_t = typename transience::edit; + + struct data_t + { + aligned_storage_for buffer; + }; + + using impl_t = combine_standard_layout_t; + + impl_t impl; + + constexpr static std::size_t sizeof_n(size_t count) + { + return immer_offsetof(impl_t, d.buffer) + sizeof(T) * count; + } + + refs_t& refs() const + { + return auto_const_cast(get(impl)); + } + + const ownee_t& ownee() const { return get(impl); } + ownee_t& ownee() { return get(impl); } + + const T* data() const { return reinterpret_cast(&impl.d.buffer); } + T* data() { return reinterpret_cast(&impl.d.buffer); } + + bool can_mutate(edit_t e) const + { + return refs().unique() + || ownee().can_mutate(e); + } + + static void delete_n(node_t* p, size_t sz, size_t cap) + { + destroy_n(p->data(), sz); + heap::deallocate(sizeof_n(cap), p); + } + + + static node_t* make_n(size_t n) + { + return new (heap::allocate(sizeof_n(n))) node_t{}; + } + + static node_t* make_e(edit_t e, size_t n) + { + auto p = make_n(n); + p->ownee() = e; + return p; + } + + static node_t* fill_n(size_t n, T v) + { + auto p = make_n(n); + try { + std::uninitialized_fill_n(p->data(), n, v); + return p; + } catch (...) { + heap::deallocate(sizeof_n(n), p); + throw; + } + } + + template + static node_t* copy_n(size_t n, Iter first, Iter last) + { + auto p = make_n(n); + try { + std::uninitialized_copy(first, last, p->data()); + return p; + } catch (...) { + heap::deallocate(sizeof_n(n), p); + throw; + } + } + + static node_t* copy_n(size_t n, node_t* p, size_t count) + { + return copy_n(n, p->data(), p->data() + count); + } + + template + static node_t* copy_e(edit_t e, size_t n, Iter first, Iter last) + { + auto p = copy_n(n, first, last); + p->ownee() = e; + return p; + } + + static node_t* copy_e(edit_t e, size_t n, node_t* p, size_t count) + { + return copy_e(e, n, p->data(), p->data() + count); + } +}; + +} // namespace arrays +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/arrays/with_capacity.hpp b/src/immer/detail/arrays/with_capacity.hpp new file mode 100644 index 000000000000..fc97f1f9f4d2 --- /dev/null +++ b/src/immer/detail/arrays/with_capacity.hpp @@ -0,0 +1,297 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { +namespace detail { +namespace arrays { + +template +struct with_capacity +{ + using no_capacity_t = no_capacity; + + using node_t = node; + using edit_t = typename MemoryPolicy::transience_t::edit; + using size_t = std::size_t; + + node_t* ptr; + size_t size; + size_t capacity; + + static const with_capacity empty; + + with_capacity(node_t* p, size_t s, size_t c) + : ptr{p}, size{s}, capacity{c} + {} + + with_capacity(const with_capacity& other) + : with_capacity{other.ptr, other.size, other.capacity} + { + inc(); + } + + with_capacity(const no_capacity_t& other) + : with_capacity{other.ptr, other.size, other.size} + { + inc(); + } + + with_capacity(with_capacity&& other) + : with_capacity{empty} + { + swap(*this, other); + } + + with_capacity& operator=(const with_capacity& other) + { + auto next = other; + swap(*this, next); + return *this; + } + + with_capacity& operator=(with_capacity&& other) + { + swap(*this, other); + return *this; + } + + friend void swap(with_capacity& x, with_capacity& y) + { + using std::swap; + swap(x.ptr, y.ptr); + swap(x.size, y.size); + swap(x.capacity, y.capacity); + } + + ~with_capacity() + { + dec(); + } + + void inc() + { + using immer::detail::get; + ptr->refs().inc(); + } + + void dec() + { + using immer::detail::get; + if (ptr->refs().dec()) + node_t::delete_n(ptr, size, capacity); + } + + const T* data() const { return ptr->data(); } + T* data() { return ptr->data(); } + + operator no_capacity_t() const + { + if (size == capacity) { + ptr->refs().inc(); + return { ptr, size }; + } else { + return { node_t::copy_n(size, ptr, size), size }; + } + } + + template + static with_capacity from_range(Iter first, Iter last) + { + auto count = static_cast(std::distance(first, last)); + return { + node_t::copy_n(count, first, last), + count, + count + }; + } + + template + static with_capacity from_initializer_list(std::initializer_list values) + { + using namespace std; + return from_range(begin(values), end(values)); + } + + static with_capacity from_fill(size_t n, T v) + { + return { node_t::fill_n(n, v), n, n }; + } + + template + void for_each_chunk(Fn&& fn) const + { + std::forward(fn)(data(), data() + size); + } + + template + bool for_each_chunk_p(Fn&& fn) const + { + return std::forward(fn)(data(), data() + size); + } + + const T& get(std::size_t index) const + { + return data()[index]; + } + + const T& get_check(std::size_t index) const + { + if (index >= size) + throw std::out_of_range{"out of range"}; + return data()[index]; + } + + bool equals(const with_capacity& other) const + { + return ptr == other.ptr || + (size == other.size && + std::equal(data(), data() + size, other.data())); + } + + static size_t recommend_up(size_t sz, size_t cap) + { + auto max = std::numeric_limits::max(); + return + sz <= cap ? cap : + cap >= max / 2 ? max + /* otherwise */ : std::max(2 * cap, sz); + } + + static size_t recommend_down(size_t sz, size_t cap) + { + return sz == 0 ? 1 : + sz < cap / 2 ? sz * 2 : + /* otherwise */ cap; + } + + with_capacity push_back(T value) const + { + auto cap = recommend_up(size + 1, capacity); + auto p = node_t::copy_n(cap, ptr, size); + try { + new (p->data() + size) T{std::move(value)}; + return { p, size + 1, cap }; + } catch (...) { + node_t::delete_n(p, size, cap); + throw; + } + } + + void push_back_mut(edit_t e, T value) + { + if (ptr->can_mutate(e) && capacity > size) { + new (data() + size) T{std::move(value)}; + ++size; + } else { + auto cap = recommend_up(size + 1, capacity); + auto p = node_t::copy_e(e, cap, ptr, size); + try { + new (p->data() + size) T{std::move(value)}; + *this = { p, size + 1, cap }; + } catch (...) { + node_t::delete_n(p, size, cap); + throw; + } + } + } + + with_capacity assoc(std::size_t idx, T value) const + { + auto p = node_t::copy_n(capacity, ptr, size); + try { + p->data()[idx] = std::move(value); + return { p, size, capacity }; + } catch (...) { + node_t::delete_n(p, size, capacity); + throw; + } + } + + void assoc_mut(edit_t e, std::size_t idx, T value) + { + if (ptr->can_mutate(e)) { + data()[idx] = std::move(value); + } else { + auto p = node_t::copy_n(capacity, ptr, size); + try { + p->data()[idx] = std::move(value); + *this = { p, size, capacity }; + } catch (...) { + node_t::delete_n(p, size, capacity); + throw; + } + } + } + + template + with_capacity update(std::size_t idx, Fn&& op) const + { + auto p = node_t::copy_n(capacity, ptr, size); + try { + auto& elem = p->data()[idx]; + elem = std::forward(op)(std::move(elem)); + return { p, size, capacity }; + } catch (...) { + node_t::delete_n(p, size, capacity); + throw; + } + } + + template + void update_mut(edit_t e, std::size_t idx, Fn&& op) + { + if (ptr->can_mutate(e)) { + auto& elem = data()[idx]; + elem = std::forward(op)(std::move(elem)); + } else { + auto p = node_t::copy_e(e, capacity, ptr, size); + try { + auto& elem = p->data()[idx]; + elem = std::forward(op)(std::move(elem)); + *this = { p, size, capacity }; + } catch (...) { + node_t::delete_n(p, size, capacity); + throw; + } + } + } + + with_capacity take(std::size_t sz) const + { + auto cap = recommend_down(sz, capacity); + auto p = node_t::copy_n(cap, ptr, sz); + return { p, sz, cap }; + } + + void take_mut(edit_t e, std::size_t sz) + { + if (ptr->can_mutate(e)) { + destroy_n(data() + size, size - sz); + size = sz; + } else { + auto cap = recommend_down(sz, capacity); + auto p = node_t::copy_e(e, cap, ptr, sz); + *this = { p, sz, cap }; + } + } +}; + +template +const with_capacity with_capacity::empty = { + node_t::make_n(1), + 0, + 1, +}; + +} // namespace arrays +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/combine_standard_layout.hpp b/src/immer/detail/combine_standard_layout.hpp new file mode 100644 index 000000000000..be8e698accb2 --- /dev/null +++ b/src/immer/detail/combine_standard_layout.hpp @@ -0,0 +1,196 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +#if __GNUC__ == 7 || __GNUC_MINOR__ == 1 +#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 1 +#define immer_offsetof(st, m) ((std::size_t) &(((st*)0)->m)) +#else +#define IMMER_BROKEN_STANDARD_LAYOUT_DETECTION 0 +#define immer_offsetof offsetof +#endif + +namespace immer { +namespace detail { + +// +// Metafunction that returns a standard layout struct that combines +// all the standard layout types in `Ts...`, while making sure that +// empty base optimizations are used. +// +// To query a part of the type do `get(x)`; +// +// This is useful when putting together a type that merges various +// types coming from different policies. Some of them might be empty, +// so we shall enable empty base optimizations. But if we just +// inherit from all of them, we would break the "standard layout" +// rules, preventing us from using `offseof(...)`. So metafunction +// will generate the type by sometimes inheriting, sometimes adding as +// member. +// +// Note that the types are added to the combined type from right to +// left! +// +template +struct combine_standard_layout; + +template +using combine_standard_layout_t = typename combine_standard_layout::type; + +namespace csl { + +template +struct type_t {}; + +template +U& get(T& x); + +template +const U& get(const T& x); + +template +struct inherit +{ + struct type : T, Next + { + using Next::get_; + + template + friend decltype(auto) get(type& x) { return x.get_(type_t{}); } + template + friend decltype(auto) get(const type& x) { return x.get_(type_t{}); } + + T& get_(type_t) { return *this; } + const T& get_(type_t) const { return *this; } + }; +}; + +template +struct inherit +{ + struct type : T + { + template + friend decltype(auto) get(type& x) { return x.get_(type_t{}); } + template + friend decltype(auto) get(const type& x) { return x.get_(type_t{}); } + + T& get_(type_t) { return *this; } + const T& get_(type_t) const { return *this; } + }; +}; + +template +struct member +{ + struct type : Next + { + T d; + + using Next::get_; + + template + friend decltype(auto) get(type& x) { return x.get_(type_t{}); } + template + friend decltype(auto) get(const type& x) { return x.get_(type_t{}); } + + T& get_(type_t) { return d; } + const T& get_(type_t) const { return d; } + }; +}; + +template +struct member +{ + struct type + { + T d; + + template + friend decltype(auto) get(type& x) { return x.get_(type_t{}); } + template + friend decltype(auto) get(const type& x) { return x.get_(type_t{}); } + + T& get_(type_t) { return d; } + const T& get_(type_t) const { return d; } + }; +}; + +template +struct member_two +{ + struct type + { + Next n; + T d; + + template + friend decltype(auto) get(type& x) { return x.get_(type_t{}); } + template + friend decltype(auto) get(const type& x) { return x.get_(type_t{}); } + + T& get_(type_t) { return d; } + const T& get_(type_t) const { return d; } + + template + auto get_(type_t t) -> decltype(auto) { return n.get_(t); } + template + auto get_(type_t t) const -> decltype(auto) { return n.get_(t); } + }; +}; + +template +struct combine_standard_layout_aux; + +template +struct combine_standard_layout_aux +{ + static_assert(std::is_standard_layout::value, ""); + + using type = typename std::conditional_t< + std::is_empty::value, + csl::inherit, + csl::member>::type; +}; + +template +struct combine_standard_layout_aux +{ + static_assert(std::is_standard_layout::value, ""); + + using this_t = T; + using next_t = typename combine_standard_layout_aux::type; + + static constexpr auto empty_this = std::is_empty::value; + static constexpr auto empty_next = std::is_empty::value; + + using type = typename std::conditional_t< + empty_this, inherit, + std::conditional_t< + empty_next, member, + member_two>>::type; +}; + +} // namespace csl + +using csl::get; + +template +struct combine_standard_layout +{ + using type = typename csl::combine_standard_layout_aux::type; +#if !IMMER_BROKEN_STANDARD_LAYOUT_DETECTION + static_assert(std::is_standard_layout::value, ""); +#endif +}; + +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/hamts/bits.hpp b/src/immer/detail/hamts/bits.hpp new file mode 100644 index 000000000000..a308b1517255 --- /dev/null +++ b/src/immer/detail/hamts/bits.hpp @@ -0,0 +1,55 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { +namespace detail { +namespace hamts { + +using bits_t = std::uint32_t; +using bitmap_t = std::uint32_t; +using count_t = std::uint32_t; +using shift_t = std::uint32_t; +using size_t = std::size_t; +using hash_t = std::size_t; + +template +constexpr T branches = T{1} << B; + +template +constexpr T mask = branches - 1; + +template +constexpr T max_depth = (sizeof(hash_t) * 8 + B - 1) / B; + +template +constexpr T max_shift = max_depth * B; + +#define IMMER_HAS_BUILTIN_POPCOUNT 1 + +inline count_t popcount(bitmap_t x) +{ +#if IMMER_HAS_BUILTIN_POPCOUNT + return __builtin_popcount(x); +#else + // More alternatives: + // https://en.wikipedia.org/wiki/Hamming_weight + // http://wm.ite.pl/articles/sse-popcount.html + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + x = x - ((x >> 1) & 0x55555555); + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + return ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; +#endif +} + +} // namespace hamts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/hamts/champ.hpp b/src/immer/detail/hamts/champ.hpp new file mode 100644 index 000000000000..fb5c9bb6f974 --- /dev/null +++ b/src/immer/detail/hamts/champ.hpp @@ -0,0 +1,474 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +#include + +namespace immer { +namespace detail { +namespace hamts { + +template +struct champ +{ + static_assert(branches <= sizeof(bitmap_t) * 8, ""); + + static constexpr auto bits = B; + + using node_t = node; + + node_t* root; + size_t size; + + static const champ empty; + + champ(node_t* r, size_t sz) + : root{r}, size{sz} + { + } + + champ(const champ& other) + : champ{other.root, other.size} + { + inc(); + } + + champ(champ&& other) + : champ{empty} + { + swap(*this, other); + } + + champ& operator=(const champ& other) + { + auto next = other; + swap(*this, next); + return *this; + } + + champ& operator=(champ&& other) + { + swap(*this, other); + return *this; + } + + friend void swap(champ& x, champ& y) + { + using std::swap; + swap(x.root, y.root); + swap(x.size, y.size); + } + + ~champ() + { + dec(); + } + + void inc() const + { + root->inc(); + } + + void dec() const + { + if (root->dec()) + node_t::delete_deep(root, 0); + } + + template + void for_each_chunk(Fn&& fn) const + { + for_each_chunk_traversal(root, 0, fn); + } + + template + void for_each_chunk_traversal(node_t* node, count_t depth, Fn&& fn) const + { + if (depth < max_depth) { + auto datamap = node->datamap(); + if (datamap) + fn(node->values(), node->values() + popcount(datamap)); + auto nodemap = node->nodemap(); + if (nodemap) { + auto fst = node->children(); + auto lst = fst + popcount(nodemap); + for (; fst != lst; ++fst) + for_each_chunk_traversal(*fst, depth + 1, fn); + } + } else { + fn(node->collisions(), node->collisions() + node->collision_count()); + } + } + + template + decltype(auto) get(const K& k) const + { + auto node = root; + auto hash = Hash{}(k); + for (auto i = count_t{}; i < max_depth; ++i) { + auto bit = 1 << (hash & mask); + if (node->nodemap() & bit) { + auto offset = popcount(node->nodemap() & (bit - 1)); + node = node->children() [offset]; + hash = hash >> B; + } else if (node->datamap() & bit) { + auto offset = popcount(node->datamap() & (bit - 1)); + auto val = node->values() + offset; + if (Equal{}(*val, k)) + return Project{}(*val); + else + return Default{}(); + } else { + return Default{}(); + } + } + auto fst = node->collisions(); + auto lst = fst + node->collision_count(); + for (; fst != lst; ++fst) + if (Equal{}(*fst, k)) + return Project{}(*fst); + return Default{}(); + } + + std::pair + do_add(node_t* node, T v, hash_t hash, shift_t shift) const + { + if (shift == max_shift) { + auto fst = node->collisions(); + auto lst = fst + node->collision_count(); + for (; fst != lst; ++fst) + if (Equal{}(*fst, v)) + return { + node_t::copy_collision_replace(node, fst, std::move(v)), + false + }; + return { + node_t::copy_collision_insert(node, std::move(v)), + true + }; + } else { + auto idx = (hash & (mask << shift)) >> shift; + auto bit = 1 << idx; + if (node->nodemap() & bit) { + auto offset = popcount(node->nodemap() & (bit - 1)); + auto result = do_add(node->children() [offset], + std::move(v), hash, + shift + B); + try { + result.first = node_t::copy_inner_replace( + node, offset, result.first); + return result; + } catch (...) { + node_t::delete_deep_shift(result.first, shift + B); + throw; + } + } else if (node->datamap() & bit) { + auto offset = popcount(node->datamap() & (bit - 1)); + auto val = node->values() + offset; + if (Equal{}(*val, v)) + return { + node_t::copy_inner_replace_value( + node, offset, std::move(v)), + false + }; + else { + auto child = node_t::make_merged(shift + B, + std::move(v), hash, + *val, Hash{}(*val)); + try { + return { + node_t::copy_inner_replace_merged( + node, bit, offset, child), + true + }; + } catch (...) { + node_t::delete_deep_shift(child, shift + B); + throw; + } + } + } else { + return { + node_t::copy_inner_insert_value(node, bit, std::move(v)), + true + }; + } + } + } + + champ add(T v) const + { + auto hash = Hash{}(v); + auto res = do_add(root, std::move(v), hash, 0); + auto new_size = size + (res.second ? 1 : 0); + return { res.first, new_size }; + } + + template + std::pair + do_update(node_t* node, K&& k, Fn&& fn, + hash_t hash, shift_t shift) const + { + if (shift == max_shift) { + auto fst = node->collisions(); + auto lst = fst + node->collision_count(); + for (; fst != lst; ++fst) + if (Equal{}(*fst, k)) + return { + node_t::copy_collision_replace( + node, fst, Combine{}(std::forward(k), + std::forward(fn)( + Project{}(*fst)))), + false + }; + return { + node_t::copy_collision_insert( + node, Combine{}(std::forward(k), + std::forward(fn)( + Default{}()))), + true + }; + } else { + auto idx = (hash & (mask << shift)) >> shift; + auto bit = 1 << idx; + if (node->nodemap() & bit) { + auto offset = popcount(node->nodemap() & (bit - 1)); + auto result = do_update( + node->children() [offset], k, std::forward(fn), + hash, shift + B); + try { + result.first = node_t::copy_inner_replace( + node, offset, result.first); + return result; + } catch (...) { + node_t::delete_deep_shift(result.first, shift + B); + throw; + } + } else if (node->datamap() & bit) { + auto offset = popcount(node->datamap() & (bit - 1)); + auto val = node->values() + offset; + if (Equal{}(*val, k)) + return { + node_t::copy_inner_replace_value( + node, offset, Combine{}(std::forward(k), + std::forward(fn)( + Project{}(*val)))), + false + }; + else { + auto child = node_t::make_merged( + shift + B, Combine{}(std::forward(k), + std::forward(fn)( + Default{}())), + hash, *val, Hash{}(*val)); + try { + return { + node_t::copy_inner_replace_merged( + node, bit, offset, child), + true + }; + } catch (...) { + node_t::delete_deep_shift(child, shift + B); + throw; + } + } + } else { + return { + node_t::copy_inner_insert_value( + node, bit, Combine{}(std::forward(k), + std::forward(fn)( + Default{}()))), + true + }; + } + } + } + + template + champ update(const K& k, Fn&& fn) const + { + auto hash = Hash{}(k); + auto res = do_update( + root, k, std::forward(fn), hash, 0); + auto new_size = size + (res.second ? 1 : 0); + return { res.first, new_size }; + } + + // basically: + // variant + // boo bad we are not using... C++17 :'( + struct sub_result + { + enum kind_t + { + nothing, + singleton, + tree + }; + + union data_t + { + T* singleton; + node_t* tree; + }; + + kind_t kind; + data_t data; + + sub_result() : kind{nothing} {}; + sub_result(T* x) : kind{singleton} { data.singleton = x; }; + sub_result(node_t* x) : kind{tree} { data.tree = x; }; + }; + + template + sub_result do_sub(node_t* node, const K& k, hash_t hash, shift_t shift) const + { + if (shift == max_shift) { + auto fst = node->collisions(); + auto lst = fst + node->collision_count(); + for (auto cur = fst; cur != lst; ++cur) + if (Equal{}(*cur, k)) + return node->collision_count() > 2 + ? node_t::copy_collision_remove(node, cur) + : sub_result{fst + (cur == fst)}; + return {}; + } else { + auto idx = (hash & (mask << shift)) >> shift; + auto bit = 1 << idx; + if (node->nodemap() & bit) { + auto offset = popcount(node->nodemap() & (bit - 1)); + auto result = do_sub(node->children() [offset], + k, hash, shift + B); + switch (result.kind) { + case sub_result::nothing: + return {}; + case sub_result::singleton: + return node->datamap() == 0 && + popcount(node->nodemap()) == 1 && + shift > 0 + ? result + : node_t::copy_inner_replace_inline( + node, bit, offset, *result.data.singleton); + case sub_result::tree: + try { + return node_t::copy_inner_replace(node, offset, + result.data.tree); + } catch (...) { + node_t::delete_deep_shift(result.data.tree, shift + B); + throw; + } + } + } else if (node->datamap() & bit) { + auto offset = popcount(node->datamap() & (bit - 1)); + auto val = node->values() + offset; + if (Equal{}(*val, k)) { + auto nv = popcount(node->datamap()); + if (node->nodemap() || nv > 2) + return node_t::copy_inner_remove_value(node, bit, offset); + else if (nv == 2) { + return shift > 0 + ? sub_result{node->values() + !offset} + : node_t::make_inner_n(0, + node->datamap() & ~bit, + node->values()[!offset]); + } else { + assert(shift == 0); + return empty.root->inc(); + } + } + } + return {}; + } + } + + template + champ sub(const K& k) const + { + auto hash = Hash{}(k); + auto res = do_sub(root, k, hash, 0); + switch (res.kind) { + case sub_result::nothing: + return *this; + case sub_result::tree: + return { + res.data.tree, + size - 1 + }; + default: + IMMER_UNREACHABLE; + } + } + + template + bool equals(const champ& other) const + { + return size == other.size && equals_tree(root, other.root, 0); + } + + template + static bool equals_tree(const node_t* a, const node_t* b, count_t depth) + { + if (a == b) + return true; + else if (depth == max_depth) { + auto nv = a->collision_count(); + return nv == b->collision_count() && + equals_collisions(a->collisions(), b->collisions(), nv); + } else { + if (a->nodemap() != b->nodemap() || + a->datamap() != b->datamap()) + return false; + auto n = popcount(a->nodemap()); + for (auto i = count_t{}; i < n; ++i) + if (!equals_tree(a->children()[i], b->children()[i], depth + 1)) + return false; + auto nv = popcount(a->datamap()); + return equals_values(a->values(), b->values(), nv); + } + } + + template + static bool equals_values(const T* a, const T* b, count_t n) + { + return std::equal(a, a + n, b, Eq{}); + } + + template + static bool equals_collisions(const T* a, const T* b, count_t n) + { + auto ae = a + n; + auto be = b + n; + for (; a != ae; ++a) { + for (auto fst = b; fst != be; ++fst) + if (Eq{}(*a, *fst)) + goto good; + return false; + good: continue; + } + return true; + } +}; + +template +const champ champ::empty = { + node_t::make_inner_n(0), + 0, +}; + +} // namespace hamts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/hamts/champ_iterator.hpp b/src/immer/detail/hamts/champ_iterator.hpp new file mode 100644 index 000000000000..a0f101bfaf96 --- /dev/null +++ b/src/immer/detail/hamts/champ_iterator.hpp @@ -0,0 +1,143 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { +namespace detail { +namespace hamts { + +template +struct champ_iterator + : iterator_facade, + std::forward_iterator_tag, + T, + const T&> +{ + using tree_t = champ; + using node_t = typename tree_t::node_t; + + struct end_t {}; + + champ_iterator() = default; + + champ_iterator(const tree_t& v) + : cur_ { v.root->values() } + , end_ { v.root->values() + popcount(v.root->datamap()) } + , depth_ { 0 } + { + path_[0] = &v.root; + ensure_valid_(); + } + + champ_iterator(const tree_t& v, end_t) + : cur_ { nullptr } + , end_ { nullptr } + , depth_ { 0 } + { + path_[0] = &v.root; + } + + champ_iterator(const champ_iterator& other) + : cur_ { other.cur_ } + , end_ { other.end_ } + , depth_ { other.depth_ } + { + std::copy(other.path_, other.path_ + depth_ + 1, path_); + } + +private: + friend iterator_core_access; + + T* cur_; + T* end_; + count_t depth_; + node_t* const* path_[max_depth + 1]; + + void increment() + { + ++cur_; + ensure_valid_(); + } + + bool step_down() + { + if (depth_ < max_depth) { + auto parent = *path_[depth_]; + if (parent->nodemap()) { + ++depth_; + path_[depth_] = parent->children(); + auto child = *path_[depth_]; + if (depth_ < max_depth) { + cur_ = child->values(); + end_ = cur_ + popcount(child->datamap()); + } else { + cur_ = child->collisions(); + end_ = cur_ + child->collision_count(); + } + return true; + } + } + return false; + } + + bool step_right() + { + while (depth_ > 0) { + auto parent = *path_[depth_ - 1]; + auto last = parent->children() + popcount(parent->nodemap()); + auto next = path_[depth_] + 1; + if (next < last) { + path_[depth_] = next; + auto child = *path_[depth_]; + if (depth_ < max_depth) { + cur_ = child->values(); + end_ = cur_ + popcount(child->datamap()); + } else { + cur_ = child->collisions(); + end_ = cur_ + child->collision_count(); + } + return true; + } + -- depth_; + } + return false; + } + + void ensure_valid_() + { + while (cur_ == end_) { + while (step_down()) + if (cur_ != end_) + return; + if (!step_right()) { + // end of sequence + assert(depth_ == 0); + cur_ = end_ = nullptr; + return; + } + } + } + + bool equal(const champ_iterator& other) const + { + return cur_ == other.cur_; + } + + const T& dereference() const + { + return *cur_; + } +}; + +} // namespace hamts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/hamts/node.hpp b/src/immer/detail/hamts/node.hpp new file mode 100644 index 000000000000..cfe7d195fd29 --- /dev/null +++ b/src/immer/detail/hamts/node.hpp @@ -0,0 +1,712 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +#include + +#ifdef NDEBUG +#define IMMER_HAMTS_TAGGED_NODE 0 +#else +#define IMMER_HAMTS_TAGGED_NODE 1 +#endif + +namespace immer { +namespace detail { +namespace hamts { + +template +struct node +{ + using node_t = node; + + using memory = MemoryPolicy; + using heap_policy = typename memory::heap; + using heap = typename heap_policy::type; + using transience = typename memory::transience_t; + using refs_t = typename memory::refcount; + using ownee_t = typename transience::ownee; + using edit_t = typename transience::edit; + using value_t = T; + + enum class kind_t + { + collision, + inner + }; + + struct collision_t + { + count_t count; + aligned_storage_for buffer; + }; + + struct values_data_t + { + aligned_storage_for buffer; + }; + + using values_t = combine_standard_layout_t< + values_data_t, refs_t>; + + struct inner_t + { + bitmap_t nodemap; + bitmap_t datamap; + values_t* values; + aligned_storage_for buffer; + }; + + union data_t + { + inner_t inner; + collision_t collision; + }; + + struct impl_data_t + { +#if IMMER_HAMTS_TAGGED_NODE + kind_t kind; +#endif + data_t data; + }; + + using impl_t = combine_standard_layout_t< + impl_data_t, refs_t>; + + impl_t impl; + + constexpr static std::size_t sizeof_values_n(count_t count) + { + return immer_offsetof(values_t, d.buffer) + + sizeof(values_data_t::buffer) * count; + } + + constexpr static std::size_t sizeof_collision_n(count_t count) + { + return immer_offsetof(impl_t, d.data.collision.buffer) + + sizeof(collision_t::buffer) * count; + } + + constexpr static std::size_t sizeof_inner_n(count_t count) + { + return immer_offsetof(impl_t, d.data.inner.buffer) + + sizeof(inner_t::buffer) * count; + } + +#if IMMER_HAMTS_TAGGED_NODE + kind_t kind() const + { + return impl.d.kind; + } +#endif + + auto values() + { + assert(kind() == kind_t::inner); + return (T*) &impl.d.data.inner.values->d.buffer; + } + + auto values() const + { + assert(kind() == kind_t::inner); + return (const T*) &impl.d.data.inner.values->d.buffer; + } + + auto children() + { + assert(kind() == kind_t::inner); + return (node_t**) &impl.d.data.inner.buffer; + } + + auto children() const + { + assert(kind() == kind_t::inner); + return (const node_t* const*) &impl.d.data.inner.buffer; + } + + auto datamap() const + { + assert(kind() == kind_t::inner); + return impl.d.data.inner.datamap; + } + + auto nodemap() const + { + assert(kind() == kind_t::inner); + return impl.d.data.inner.nodemap; + } + + auto collision_count() const + { + assert(kind() == kind_t::collision); + return impl.d.data.collision.count; + } + + T* collisions() + { + assert(kind() == kind_t::collision); + return (T*)&impl.d.data.collision.buffer; + } + + const T* collisions() const + { + assert(kind() == kind_t::collision); + return (const T*)&impl.d.data.collision.buffer; + } + + static refs_t& refs(const values_t* x) { return auto_const_cast(get(*x)); } + static const ownee_t& ownee(const values_t* x) { return get(*x); } + static ownee_t& ownee(values_t* x) { return get(*x); } + + static refs_t& refs(const node_t* x) { return auto_const_cast(get(x->impl)); } + static const ownee_t& ownee(const node_t* x) { return get(x->impl); } + static ownee_t& ownee(node_t* x) { return get(x->impl); } + + static node_t* make_inner_n(count_t n) + { + assert(n <= branches); + auto m = heap::allocate(sizeof_inner_n(n)); + auto p = new (m) node_t; +#if IMMER_HAMTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::inner; +#endif + p->impl.d.data.inner.nodemap = 0; + p->impl.d.data.inner.datamap = 0; + p->impl.d.data.inner.values = nullptr; + return p; + } + + static node_t* make_inner_n(count_t n, values_t* values) + { + auto p = make_inner_n(n); + if (values) { + p->impl.d.data.inner.values = values; + refs(values).inc(); + } + return p; + } + + static node_t* make_inner_n(count_t n, count_t nv) + { + assert(nv <= branches); + auto p = make_inner_n(n); + if (nv) { + try { + p->impl.d.data.inner.values = + new (heap::allocate(sizeof_values_n(nv))) values_t{}; + } catch (...) { + deallocate_inner(p, n); + throw; + } + } + return p; + } + + static node_t* make_inner_n(count_t n, count_t idx, node_t* child) + { + assert(n >= 1); + auto p = make_inner_n(n); + p->impl.d.data.inner.nodemap = 1 << idx; + p->children()[0] = child; + return p; + } + + static node_t* make_inner_n(count_t n, + bitmap_t bitmap, + T x) + { + auto p = make_inner_n(n, 1); + p->impl.d.data.inner.datamap = bitmap; + try { + new (p->values()) T{std::move(x)}; + } catch (...) { + deallocate_inner(p, n, 1); + throw; + } + return p; + } + + static node_t* make_inner_n(count_t n, + count_t idx1, T x1, + count_t idx2, T x2) + { + assert(idx1 != idx2); + auto p = make_inner_n(n, 2); + p->impl.d.data.inner.datamap = (1 << idx1) | (1 << idx2); + auto assign = [&] (auto&& x1, auto&& x2) { + auto vp = p->values(); + try { + new (vp) T{std::move(x1)}; + try { + new (vp + 1) T{std::move(x2)}; + } catch (...) { + vp->~T(); + throw; + } + } catch (...) { + deallocate_inner(p, n, 2); + throw; + } + }; + if (idx1 < idx2) + assign(x1, x2); + else + assign(x2, x1); + return p; + } + + static node_t* make_collision_n(count_t n) + { + assert(n <= branches); + auto m = heap::allocate(sizeof_collision_n(n)); + auto p = new (m) node_t; +#if IMMER_HAMTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::collision; +#endif + p->impl.d.data.collision.count = n; + return p; + } + + static node_t* make_collision(T v1, T v2) + { + auto m = heap::allocate(sizeof_collision_n(2)); + auto p = new (m) node_t; +#if IMMER_HAMTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::collision; +#endif + p->impl.d.data.collision.count = 2; + auto cols = p->collisions(); + try { + new (cols) T{std::move(v1)}; + try { + new (cols + 1) T{std::move(v2)}; + } catch (...) { + cols->~T(); + throw; + } + } catch (...) { + deallocate_collision(p, 2); + throw; + } + return p; + } + + static node_t* copy_collision_insert(node_t* src, T v) + { + assert(src->kind() == kind_t::collision); + auto n = src->collision_count(); + auto dst = make_collision_n(n + 1); + auto srcp = src->collisions(); + auto dstp = dst->collisions(); + try { + new (dstp) T{std::move(v)}; + try { + std::uninitialized_copy(srcp, srcp + n, dstp + 1); + } catch (...) { + dstp->~T(); + throw; + } + } catch (...) { + deallocate_collision(dst, n + 1); + throw; + } + return dst; + } + + static node_t* copy_collision_remove(node_t* src, T* v) + { + assert(src->kind() == kind_t::collision); + assert(src->collision_count() > 1); + auto n = src->collision_count(); + auto dst = make_collision_n(n - 1); + auto srcp = src->collisions(); + auto dstp = dst->collisions(); + try { + dstp = std::uninitialized_copy(srcp, v, dstp); + try { + std::uninitialized_copy(v + 1, srcp + n, dstp); + } catch (...) { + destroy(dst->collisions(), dstp); + throw; + } + } catch (...) { + deallocate_collision(dst, n - 1); + throw; + } + return dst; + } + + static node_t* copy_collision_replace(node_t* src, T* pos, T v) + { + assert(src->kind() == kind_t::collision); + auto n = src->collision_count(); + auto dst = make_collision_n(n); + auto srcp = src->collisions(); + auto dstp = dst->collisions(); + assert(pos >= srcp && pos < srcp + n); + try { + new (dstp) T{std::move(v)}; + try { + dstp = std::uninitialized_copy(srcp, pos, dstp + 1); + try { + std::uninitialized_copy(pos + 1, srcp + n, dstp); + } catch (...) { + destroy(dst->collisions(), dstp); + throw; + } + } catch (...) { + dst->collisions()->~T(); + throw; + } + } catch (...) { + deallocate_collision(dst, n); + throw; + } + return dst; + } + + static node_t* copy_inner_replace(node_t* src, + count_t offset, node_t* child) + { + assert(src->kind() == kind_t::inner); + auto n = popcount(src->nodemap()); + auto dst = make_inner_n(n, src->impl.d.data.inner.values); + auto srcp = src->children(); + auto dstp = dst->children(); + dst->impl.d.data.inner.datamap = src->datamap(); + dst->impl.d.data.inner.nodemap = src->nodemap(); + std::uninitialized_copy(srcp, srcp + n, dstp); + inc_nodes(srcp, n); + srcp[offset]->dec_unsafe(); + dstp[offset] = child; + return dst; + } + + static node_t* copy_inner_replace_value(node_t* src, + count_t offset, T v) + { + assert(src->kind() == kind_t::inner); + assert(offset < popcount(src->datamap())); + auto n = popcount(src->nodemap()); + auto nv = popcount(src->datamap()); + auto dst = make_inner_n(n, nv); + dst->impl.d.data.inner.datamap = src->datamap(); + dst->impl.d.data.inner.nodemap = src->nodemap(); + try { + std::uninitialized_copy( + src->values(), src->values() + nv, dst->values()); + try { + dst->values()[offset] = std::move(v); + } catch (...) { + destroy_n(dst->values(), nv); + throw; + } + } catch (...) { + deallocate_inner(dst, n, nv); + throw; + } + inc_nodes(src->children(), n); + std::uninitialized_copy( + src->children(), src->children() + n, dst->children()); + return dst; + } + + static node_t* copy_inner_replace_merged( + node_t* src, bitmap_t bit, count_t voffset, node_t* node) + { + assert(src->kind() == kind_t::inner); + assert(!(src->nodemap() & bit)); + assert(src->datamap() & bit); + assert(voffset == popcount(src->datamap() & (bit - 1))); + auto n = popcount(src->nodemap()); + auto nv = popcount(src->datamap()); + auto dst = make_inner_n(n + 1, nv - 1); + auto noffset = popcount(src->nodemap() & (bit - 1)); + dst->impl.d.data.inner.datamap = src->datamap() & ~bit; + dst->impl.d.data.inner.nodemap = src->nodemap() | bit; + try { + std::uninitialized_copy( + src->values(), src->values() + voffset, + dst->values()); + try { + std::uninitialized_copy( + src->values() + voffset + 1, src->values() + nv, + dst->values() + voffset); + } catch (...) { + destroy_n(dst->values(), voffset); + throw; + } + } catch (...) { + deallocate_inner(dst, n + 1, nv - 1); + throw; + } + inc_nodes(src->children(), n); + std::uninitialized_copy( + src->children(), src->children() + noffset, + dst->children()); + std::uninitialized_copy( + src->children() + noffset, src->children() + n, + dst->children() + noffset + 1); + dst->children()[noffset] = node; + return dst; + } + + static node_t* copy_inner_replace_inline( + node_t* src, bitmap_t bit, count_t noffset, T value) + { + assert(src->kind() == kind_t::inner); + assert(!(src->datamap() & bit)); + assert(src->nodemap() & bit); + assert(noffset == popcount(src->nodemap() & (bit - 1))); + auto n = popcount(src->nodemap()); + auto nv = popcount(src->datamap()); + auto dst = make_inner_n(n - 1, nv + 1); + auto voffset = popcount(src->datamap() & (bit - 1)); + dst->impl.d.data.inner.nodemap = src->nodemap() & ~bit; + dst->impl.d.data.inner.datamap = src->datamap() | bit; + try { + std::uninitialized_copy( + src->values(), src->values() + voffset, + dst->values()); + try { + new (dst->values() + voffset) T{std::move(value)}; + try { + std::uninitialized_copy( + src->values() + voffset, src->values() + nv, + dst->values() + voffset + 1); + } catch (...) { + dst->values()[voffset].~T(); + throw; + } + } catch (...) { + destroy_n(dst->values(), voffset); + throw; + } + } catch (...) { + deallocate_inner(dst, n - 1, nv + 1); + throw; + } + inc_nodes(src->children(), n); + src->children()[noffset]->dec_unsafe(); + std::uninitialized_copy( + src->children(), src->children() + noffset, + dst->children()); + std::uninitialized_copy( + src->children() + noffset + 1, src->children() + n, + dst->children() + noffset); + return dst; + } + + static node_t* copy_inner_remove_value( + node_t* src, bitmap_t bit, count_t voffset) + { + assert(src->kind() == kind_t::inner); + assert(!(src->nodemap() & bit)); + assert(src->datamap() & bit); + assert(voffset == popcount(src->datamap() & (bit - 1))); + auto n = popcount(src->nodemap()); + auto nv = popcount(src->datamap()); + auto dst = make_inner_n(n, nv - 1); + dst->impl.d.data.inner.datamap = src->datamap() & ~bit; + dst->impl.d.data.inner.nodemap = src->nodemap(); + try { + std::uninitialized_copy( + src->values(), src->values() + voffset, + dst->values()); + try { + std::uninitialized_copy( + src->values() + voffset + 1, src->values() + nv, + dst->values() + voffset); + } catch (...) { + destroy_n(dst->values(), voffset); + throw; + } + } catch (...) { + deallocate_inner(dst, n, nv - 1); + throw; + } + inc_nodes(src->children(), n); + std::uninitialized_copy( + src->children(), src->children() + n, dst->children()); + return dst; + } + + static node_t* copy_inner_insert_value(node_t* src, bitmap_t bit, T v) + { + assert(src->kind() == kind_t::inner); + auto n = popcount(src->nodemap()); + auto nv = popcount(src->datamap()); + auto offset = popcount(src->datamap() & (bit - 1)); + auto dst = make_inner_n(n, nv + 1); + dst->impl.d.data.inner.datamap = src->datamap() | bit; + dst->impl.d.data.inner.nodemap = src->nodemap(); + try { + std::uninitialized_copy( + src->values(), src->values() + offset, dst->values()); + try { + new (dst->values() + offset) T{std::move(v)}; + try { + std::uninitialized_copy( + src->values() + offset, src->values() + nv, + dst->values() + offset + 1); + } catch (...) { + dst->values()[offset].~T(); + throw; + } + } catch (...) { + destroy_n(dst->values(), offset); + throw; + } + } catch (...) { + deallocate_inner(dst, n, nv + 1); + throw; + } + inc_nodes(src->children(), n); + std::uninitialized_copy( + src->children(), src->children() + n, dst->children()); + return dst; + } + + static node_t* make_merged(shift_t shift, + T v1, hash_t hash1, + T v2, hash_t hash2) + { + if (shift < max_shift) { + auto idx1 = hash1 & (mask << shift); + auto idx2 = hash2 & (mask << shift); + if (idx1 == idx2) { + auto merged = make_merged(shift + B, + std::move(v1), hash1, + std::move(v2), hash2); + try { + return make_inner_n(1, idx1 >> shift, merged); + } catch (...) { + delete_deep_shift(merged, shift + B); + throw; + } + } else { + return make_inner_n(0, + idx1 >> shift, std::move(v1), + idx2 >> shift, std::move(v2)); + } + } else { + return make_collision(std::move(v1), std::move(v2)); + } + } + + node_t* inc() + { + refs(this).inc(); + return this; + } + + const node_t* inc() const + { + refs(this).inc(); + return this; + } + + bool dec() const { return refs(this).dec(); } + void dec_unsafe() const { refs(this).dec_unsafe(); } + + static void inc_nodes(node_t** p, count_t n) + { + for (auto i = p, e = i + n; i != e; ++i) + refs(*i).inc(); + } + + static void delete_values(values_t* p, count_t n) + { + assert(p); + destroy_n(&p->d.buffer, n); + deallocate_values(p, n); + } + + static void delete_inner(node_t* p) + { + assert(p); + assert(p->kind() == kind_t::inner); + auto vp = p->impl.d.data.inner.values; + if (vp && refs(vp).dec()) + delete_values(vp, popcount(p->datamap())); + deallocate_inner(p, popcount(p->nodemap())); + } + + static void delete_collision(node_t* p) + { + assert(p); + assert(p->kind() == kind_t::collision); + auto n = p->collision_count(); + destroy_n(p->collisions(), n); + deallocate_collision(p, n); + } + + static void delete_deep(node_t* p, shift_t s) + { + if (s == max_depth) + delete_collision(p); + else { + auto fst = p->children(); + auto lst = fst + popcount(p->nodemap()); + for (; fst != lst; ++fst) + if ((*fst)->dec()) + delete_deep(*fst, s + 1); + delete_inner(p); + } + } + + static void delete_deep_shift(node_t* p, shift_t s) + { + if (s == max_shift) + delete_collision(p); + else { + auto fst = p->children(); + auto lst = fst + popcount(p->nodemap()); + for (; fst != lst; ++fst) + if ((*fst)->dec()) + delete_deep_shift(*fst, s + B); + delete_inner(p); + } + } + + static void deallocate_values(values_t* p, count_t n) + { + destroy_n((T*) &p->d.buffer, n); + heap::deallocate(node_t::sizeof_values_n(n), p); + } + + static void deallocate_collision(node_t* p, count_t n) + { + destroy_n(p->collisions(), n); + heap::deallocate(node_t::sizeof_collision_n(n), p); + } + + static void deallocate_inner(node_t* p, count_t n) + { + heap::deallocate(node_t::sizeof_inner_n(n), p); + } + + static void deallocate_inner(node_t* p, count_t n, count_t nv) + { + deallocate_values(p->impl.d.data.inner.values, nv); + heap::deallocate(node_t::sizeof_inner_n(n), p); + } +}; + +} // namespace hamts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/iterator_facade.hpp b/src/immer/detail/iterator_facade.hpp new file mode 100644 index 000000000000..985b2f17ae65 --- /dev/null +++ b/src/immer/detail/iterator_facade.hpp @@ -0,0 +1,202 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +namespace immer { +namespace detail { + +struct iterator_core_access +{ + template + static decltype(auto) dereference(T&& x) + { return x.dereference(); } + + template + static decltype(auto) increment(T&& x) + { return x.increment(); } + + template + static decltype(auto) decrement(T&& x) + { return x.decrement(); } + + template + static decltype(auto) equal(T1&& x1, T2&& x2) + { return x1.equal(x2); } + + template + static decltype(auto) advance(T&& x, D d) + { return x.advance(d); } + + template + static decltype(auto) distance_to(T1&& x1, T2&& x2) + { return x1.distance_to(x2); } +}; + +/*! + * Minimalistic reimplementation of boost::iterator_facade + */ +template +class iterator_facade + : public std::iterator +{ +protected: + using access_t = iterator_core_access; + + constexpr static auto is_random_access = + std::is_base_of::value; + constexpr static auto is_bidirectional = + std::is_base_of::value; + + class reference_proxy + { + friend iterator_facade; + DerivedT iter_; + + reference_proxy(DerivedT iter) + : iter_{std::move(iter)} {} + public: + operator ReferenceT() const { return *iter_; } + }; + + const DerivedT& derived() const + { + static_assert(std::is_base_of::value, + "must pass a derived thing"); + return *static_cast(this); + } + DerivedT& derived() + { + static_assert(std::is_base_of::value, + "must pass a derived thing"); + return *static_cast(this); + } + +public: + ReferenceT operator*() const + { + return access_t::dereference(derived()); + } + PointerT operator->() const + { + return &access_t::dereference(derived()); + } + reference_proxy operator[](DifferenceTypeT n) const + { + static_assert(is_random_access, ""); + return derived() + n; + } + + bool operator==(const DerivedT& rhs) const + { + return access_t::equal(derived(), rhs); + } + bool operator!=(const DerivedT& rhs) const + { + return !access_t::equal(derived(), rhs); + } + + DerivedT& operator++() + { + access_t::increment(derived()); + return derived(); + } + DerivedT operator++(int) + { + auto tmp = derived(); + access_t::increment(derived()); + return tmp; + } + + DerivedT& operator--() + { + static_assert(is_bidirectional || is_random_access, ""); + access_t::decrement(derived()); + return derived(); + } + DerivedT operator--(int) + { + static_assert(is_bidirectional || is_random_access, ""); + auto tmp = derived(); + access_t::decrement(derived()); + return tmp; + } + + DerivedT& operator+=(DifferenceTypeT n) + { + access_t::advance(derived(), n); + return derived(); + } + DerivedT& operator-=(DifferenceTypeT n) + { + access_t::advance(derived(), -n); + return derived(); + } + + DerivedT operator+(DifferenceTypeT n) const + { + static_assert(is_random_access, ""); + auto tmp = derived(); + return tmp += n; + } + friend DerivedT operator+(DifferenceTypeT n, const DerivedT& i) + { + static_assert(is_random_access, ""); + return i + n; + } + DerivedT operator-(DifferenceTypeT n) const + { + static_assert(is_random_access, ""); + auto tmp = derived(); + return tmp -= n; + } + DifferenceTypeT operator-(const DerivedT& rhs) const + { + static_assert(is_random_access, ""); + return access_t::distance_to(rhs, derived()); + } + + bool operator<(const DerivedT& rhs) const + { + static_assert(is_random_access, ""); + return access_t::distance_to(derived(), rhs) > 0; + } + bool operator<=(const DerivedT& rhs) const + { + static_assert(is_random_access, ""); + return access_t::distance_to(derived(), rhs) >= 0; + } + bool operator>(const DerivedT& rhs) const + { + static_assert(is_random_access, ""); + return access_t::distance_to(derived(), rhs) < 0; + } + bool operator>=(const DerivedT& rhs) const + { + static_assert(is_random_access, ""); + return access_t::distance_to(derived(), rhs) <= 0; + } +}; + +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/bits.hpp b/src/immer/detail/rbts/bits.hpp new file mode 100644 index 000000000000..549319ae793a --- /dev/null +++ b/src/immer/detail/rbts/bits.hpp @@ -0,0 +1,33 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { +namespace detail { +namespace rbts { + +using bits_t = std::uint32_t; +using shift_t = std::uint32_t; +using count_t = std::uint32_t; +using size_t = std::size_t; + +template +constexpr T branches = T{1} << B; + +template +constexpr T mask = branches - 1; + +template +constexpr shift_t endshift = shift_t{BL} - shift_t{B}; + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/node.hpp b/src/immer/detail/rbts/node.hpp new file mode 100644 index 000000000000..229b1d9d48d7 --- /dev/null +++ b/src/immer/detail/rbts/node.hpp @@ -0,0 +1,942 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef NDEBUG +#define IMMER_RBTS_TAGGED_NODE 0 +#else +#define IMMER_RBTS_TAGGED_NODE 1 +#endif + +namespace immer { +namespace detail { +namespace rbts { + +template +struct node +{ + static constexpr auto bits = B; + static constexpr auto bits_leaf = BL; + + using node_t = node; + using memory = MemoryPolicy; + using heap_policy = typename memory::heap; + using transience = typename memory::transience_t; + using refs_t = typename memory::refcount; + using ownee_t = typename transience::ownee; + using edit_t = typename transience::edit; + using value_t = T; + + static constexpr bool embed_relaxed = memory::prefer_fewer_bigger_objects; + + enum class kind_t + { + leaf, + inner + }; + + struct relaxed_data_t + { + count_t count; + size_t sizes[branches]; + }; + + using relaxed_data_with_meta_t = + combine_standard_layout_t; + + using relaxed_data_no_meta_t = + combine_standard_layout_t; + + using relaxed_t = std::conditional_t; + + struct leaf_t + { + aligned_storage_for buffer; + }; + + struct inner_t + { + relaxed_t* relaxed; + aligned_storage_for buffer; + }; + + union data_t + { + inner_t inner; + leaf_t leaf; + }; + + struct impl_data_t + { +#if IMMER_RBTS_TAGGED_NODE + kind_t kind; +#endif + data_t data; + }; + + using impl_t = combine_standard_layout_t< + impl_data_t, refs_t, ownee_t>; + + impl_t impl; + + // assume that we need to keep headroom space in the node when we + // are doing reference counting, since any node may become + // transient when it has only one reference + constexpr static bool keep_headroom = !std::is_empty{}; + + constexpr static std::size_t sizeof_packed_leaf_n(count_t count) + { + return immer_offsetof(impl_t, d.data.leaf.buffer) + + sizeof(leaf_t::buffer) * count; + } + + constexpr static std::size_t sizeof_packed_inner_n(count_t count) + { + return immer_offsetof(impl_t, d.data.inner.buffer) + + sizeof(inner_t::buffer) * count; + } + + constexpr static std::size_t sizeof_packed_relaxed_n(count_t count) + { + return immer_offsetof(relaxed_t, d.sizes) + + sizeof(size_t) * count; + } + + constexpr static std::size_t sizeof_packed_inner_r_n(count_t count) + { + return embed_relaxed + ? sizeof_packed_inner_n(count) + sizeof_packed_relaxed_n(count) + : sizeof_packed_inner_n(count); + } + + constexpr static std::size_t max_sizeof_leaf = + sizeof_packed_leaf_n(branches); + + constexpr static std::size_t max_sizeof_inner = + sizeof_packed_inner_n(branches); + + constexpr static std::size_t max_sizeof_relaxed = + sizeof_packed_relaxed_n(branches); + + constexpr static std::size_t max_sizeof_inner_r = + sizeof_packed_inner_r_n(branches); + + constexpr static std::size_t sizeof_inner_n(count_t n) + { return keep_headroom ? max_sizeof_inner : sizeof_packed_inner_n(n); } + + constexpr static std::size_t sizeof_inner_r_n(count_t n) + { return keep_headroom ? max_sizeof_inner_r : sizeof_packed_inner_r_n(n); } + + constexpr static std::size_t sizeof_relaxed_n(count_t n) + { return keep_headroom ? max_sizeof_relaxed : sizeof_packed_relaxed_n(n); } + + constexpr static std::size_t sizeof_leaf_n(count_t n) + { return keep_headroom ? max_sizeof_leaf : sizeof_packed_leaf_n(n); } + + using heap = typename heap_policy::template + optimized::type; + +#if IMMER_RBTS_TAGGED_NODE + kind_t kind() const + { + return impl.d.kind; + } +#endif + + relaxed_t* relaxed() + { + assert(kind() == kind_t::inner); + return impl.d.data.inner.relaxed; + } + + const relaxed_t* relaxed() const + { + assert(kind() == kind_t::inner); + return impl.d.data.inner.relaxed; + } + + node_t** inner() + { + assert(kind() == kind_t::inner); + return reinterpret_cast(&impl.d.data.inner.buffer); + } + + T* leaf() + { + assert(kind() == kind_t::leaf); + return reinterpret_cast(&impl.d.data.leaf.buffer); + } + + static refs_t& refs(const relaxed_t* x) { return auto_const_cast(get(*x)); } + static const ownee_t& ownee(const relaxed_t* x) { return get(*x); } + static ownee_t& ownee(relaxed_t* x) { return get(*x); } + + static refs_t& refs(const node_t* x) { return auto_const_cast(get(x->impl)); } + static const ownee_t& ownee(const node_t* x) { return get(x->impl); } + static ownee_t& ownee(node_t* x) { return get(x->impl); } + + static node_t* make_inner_n(count_t n) + { + assert(n <= branches); + auto m = heap::allocate(sizeof_inner_n(n)); + auto p = new (m) node_t; + p->impl.d.data.inner.relaxed = nullptr; +#if IMMER_RBTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::inner; +#endif + return p; + } + + static node_t* make_inner_e(edit_t e) + { + auto m = heap::allocate(max_sizeof_inner); + auto p = new (m) node_t; + ownee(p) = e; + p->impl.d.data.inner.relaxed = nullptr; +#if IMMER_RBTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::inner; +#endif + return p; + } + + static node_t* make_inner_r_n(count_t n) + { + assert(n <= branches); + auto mp = heap::allocate(sizeof_inner_r_n(n)); + auto mr = static_cast(nullptr); + if (embed_relaxed) { + mr = reinterpret_cast(mp) + sizeof_inner_n(n); + } else { + try { + mr = heap::allocate(sizeof_relaxed_n(n), norefs_tag{}); + } catch (...) { + heap::deallocate(sizeof_inner_r_n(n), mp); + throw; + } + } + auto p = new (mp) node_t; + auto r = new (mr) relaxed_t; + r->d.count = 0; + p->impl.d.data.inner.relaxed = r; +#if IMMER_RBTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::inner; +#endif + return p; + } + + static node_t* make_inner_sr_n(count_t n, relaxed_t* r) + { + return static_if( + [&] (auto) { + return node_t::make_inner_r_n(n); + }, + [&] (auto) { + auto p = new (heap::allocate(node_t::sizeof_inner_r_n(n))) node_t; + assert(r->d.count >= n); + node_t::refs(r).inc(); + p->impl.d.data.inner.relaxed = r; +#if IMMER_RBTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::inner; +#endif + return p; + }); + } + + static node_t* make_inner_r_e(edit_t e) + { + auto mp = heap::allocate(max_sizeof_inner_r); + auto mr = static_cast(nullptr); + if (embed_relaxed) { + mr = reinterpret_cast(mp) + max_sizeof_inner; + } else { + try { + mr = heap::allocate(max_sizeof_relaxed, norefs_tag{}); + } catch (...) { + heap::deallocate(max_sizeof_inner_r, mp); + throw; + } + } + auto p = new (mp) node_t; + auto r = new (mr) relaxed_t; + ownee(p) = e; + static_if([&](auto){ node_t::ownee(r) = e; }); + r->d.count = 0; + p->impl.d.data.inner.relaxed = r; +#if IMMER_RBTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::inner; +#endif + return p; + } + + static node_t* make_inner_sr_e(edit_t e, relaxed_t* r) + { + return static_if( + [&] (auto) { + return node_t::make_inner_r_e(e); + }, + [&] (auto) { + auto p = new (heap::allocate(node_t::max_sizeof_inner_r)) node_t; + node_t::refs(r).inc(); + p->impl.d.data.inner.relaxed = r; + node_t::ownee(p) = e; +#if IMMER_RBTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::inner; +#endif + return p; + }); + } + + static node_t* make_leaf_n(count_t n) + { + assert(n <= branches); + auto p = new (heap::allocate(sizeof_leaf_n(n))) node_t; +#if IMMER_RBTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::leaf; +#endif + return p; + } + + static node_t* make_leaf_e(edit_t e) + { + auto p = new (heap::allocate(max_sizeof_leaf)) node_t; + ownee(p) = e; +#if IMMER_RBTS_TAGGED_NODE + p->impl.d.kind = node_t::kind_t::leaf; +#endif + return p; + } + + static node_t* make_inner_n(count_t n, node_t* x) + { + assert(n >= 1); + auto p = make_inner_n(n); + p->inner() [0] = x; + return p; + } + + static node_t* make_inner_n(edit_t n, node_t* x) + { + assert(n >= 1); + auto p = make_inner_n(n); + p->inner() [0] = x; + return p; + } + + static node_t* make_inner_n(count_t n, node_t* x, node_t* y) + { + assert(n >= 2); + auto p = make_inner_n(n); + p->inner() [0] = x; + p->inner() [1] = y; + return p; + } + + static node_t* make_inner_r_n(count_t n, node_t* x) + { + assert(n >= 1); + auto p = make_inner_r_n(n); + auto r = p->relaxed(); + p->inner() [0] = x; + r->d.count = 1; + return p; + } + + static node_t* make_inner_r_n(count_t n, node_t* x, size_t xs) + { + assert(n >= 1); + auto p = make_inner_r_n(n); + auto r = p->relaxed(); + p->inner() [0] = x; + r->d.sizes [0] = xs; + r->d.count = 1; + return p; + } + + static node_t* make_inner_r_n(count_t n, node_t* x, node_t* y) + { + assert(n >= 2); + auto p = make_inner_r_n(n); + auto r = p->relaxed(); + p->inner() [0] = x; + p->inner() [1] = y; + r->d.count = 2; + return p; + } + + static node_t* make_inner_r_n(count_t n, + node_t* x, size_t xs, + node_t* y) + { + assert(n >= 2); + auto p = make_inner_r_n(n); + auto r = p->relaxed(); + p->inner() [0] = x; + p->inner() [1] = y; + r->d.sizes [0] = xs; + r->d.count = 2; + return p; + } + + static node_t* make_inner_r_n(count_t n, + node_t* x, size_t xs, + node_t* y, size_t ys) + { + assert(n >= 2); + auto p = make_inner_r_n(n); + auto r = p->relaxed(); + p->inner() [0] = x; + p->inner() [1] = y; + r->d.sizes [0] = xs; + r->d.sizes [1] = xs + ys; + r->d.count = 2; + return p; + } + + static node_t* make_inner_r_n(count_t n, + node_t* x, size_t xs, + node_t* y, size_t ys, + node_t* z, size_t zs) + { + assert(n >= 3); + auto p = make_inner_r_n(n); + auto r = p->relaxed(); + p->inner() [0] = x; + p->inner() [1] = y; + p->inner() [2] = z; + r->d.sizes [0] = xs; + r->d.sizes [1] = xs + ys; + r->d.sizes [2] = xs + ys + zs; + r->d.count = 3; + return p; + } + + template + static node_t* make_leaf_n(count_t n, U&& x) + { + assert(n >= 1); + auto p = make_leaf_n(n); + try { + new (p->leaf()) T{ std::forward(x) }; + } catch (...) { + heap::deallocate(node_t::sizeof_leaf_n(n), p); + throw; + } + return p; + } + + template + static node_t* make_leaf_e(edit_t e, U&& x) + { + auto p = make_leaf_e(e); + try { + new (p->leaf()) T{ std::forward(x) }; + } catch (...) { + heap::deallocate(node_t::max_sizeof_leaf, p); + throw; + } + return p; + } + + static node_t* make_path(shift_t shift, node_t* node) + { + assert(node->kind() == kind_t::leaf); + if (shift == endshift) + return node; + else { + auto n = node_t::make_inner_n(1); + try { + n->inner() [0] = make_path(shift - B, node); + } catch (...) { + heap::deallocate(node_t::sizeof_inner_n(1), n); + throw; + } + return n; + } + } + + static node_t* make_path_e(edit_t e, shift_t shift, node_t* node) + { + assert(node->kind() == kind_t::leaf); + if (shift == endshift) + return node; + else { + auto n = node_t::make_inner_e(e); + try { + n->inner() [0] = make_path_e(e, shift - B, node); + } catch (...) { + heap::deallocate(node_t::max_sizeof_inner, n); + throw; + } + return n; + } + } + + static node_t* copy_inner(node_t* src, count_t n) + { + assert(src->kind() == kind_t::inner); + auto dst = make_inner_n(n); + inc_nodes(src->inner(), n); + std::uninitialized_copy(src->inner(), src->inner() + n, dst->inner()); + return dst; + } + + static node_t* copy_inner_n(count_t allocn, node_t* src, count_t n) + { + assert(allocn >= n); + assert(src->kind() == kind_t::inner); + auto dst = make_inner_n(allocn); + return do_copy_inner(dst, src, n); + } + + static node_t* copy_inner_e(edit_t e, node_t* src, count_t n) + { + assert(src->kind() == kind_t::inner); + auto dst = make_inner_e(e); + return do_copy_inner(dst, src, n); + } + + static node_t* do_copy_inner(node_t* dst, node_t* src, count_t n) + { + assert(dst->kind() == kind_t::inner); + assert(src->kind() == kind_t::inner); + auto p = src->inner(); + inc_nodes(p, n); + std::uninitialized_copy(p, p + n, dst->inner()); + return dst; + } + + static node_t* copy_inner_r(node_t* src, count_t n) + { + assert(src->kind() == kind_t::inner); + auto dst = make_inner_r_n(n); + return do_copy_inner_r(dst, src, n); + } + + static node_t* copy_inner_r_n(count_t allocn, node_t* src, count_t n) + { + assert(allocn >= n); + assert(src->kind() == kind_t::inner); + auto dst = make_inner_r_n(allocn); + return do_copy_inner_r(dst, src, n); + } + + static node_t* copy_inner_r_e(edit_t e, node_t* src, count_t n) + { + assert(src->kind() == kind_t::inner); + auto dst = make_inner_r_e(e); + return do_copy_inner_r(dst, src, n); + } + + static node_t* copy_inner_sr_e(edit_t e, node_t* src, count_t n) + { + assert(src->kind() == kind_t::inner); + auto dst = make_inner_sr_e(e, src->relaxed()); + return do_copy_inner_sr(dst, src, n); + } + + static node_t* do_copy_inner_r(node_t* dst, node_t* src, count_t n) + { + assert(dst->kind() == kind_t::inner); + assert(src->kind() == kind_t::inner); + auto src_r = src->relaxed(); + auto dst_r = dst->relaxed(); + inc_nodes(src->inner(), n); + std::copy(src->inner(), src->inner() + n, dst->inner()); + std::copy(src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes); + dst_r->d.count = n; + return dst; + } + + static node_t* do_copy_inner_sr(node_t* dst, node_t* src, count_t n) + { + if (embed_relaxed) + return do_copy_inner_r(dst, src, n); + else { + inc_nodes(src->inner(), n); + std::copy(src->inner(), src->inner() + n, dst->inner()); + return dst; + } + } + + static node_t* copy_leaf(node_t* src, count_t n) + { + assert(src->kind() == kind_t::leaf); + auto dst = make_leaf_n(n); + try { + std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf()); + } catch (...) { + heap::deallocate(node_t::sizeof_leaf_n(n), dst); + throw; + } + return dst; + } + + static node_t* copy_leaf_e(edit_t e, node_t* src, count_t n) + { + assert(src->kind() == kind_t::leaf); + auto dst = make_leaf_e(e); + try { + std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf()); + } catch (...) { + heap::deallocate(node_t::max_sizeof_leaf, dst); + throw; + } + return dst; + } + + static node_t* copy_leaf_n(count_t allocn, node_t* src, count_t n) + { + assert(allocn >= n); + assert(src->kind() == kind_t::leaf); + auto dst = make_leaf_n(allocn); + try { + std::uninitialized_copy(src->leaf(), src->leaf() + n, dst->leaf()); + } catch (...) { + heap::deallocate(node_t::sizeof_leaf_n(allocn), dst); + throw; + } + return dst; + } + + static node_t* copy_leaf(node_t* src1, count_t n1, + node_t* src2, count_t n2) + { + assert(src1->kind() == kind_t::leaf); + assert(src2->kind() == kind_t::leaf); + auto dst = make_leaf_n(n1 + n2); + try { + std::uninitialized_copy( + src1->leaf(), src1->leaf() + n1, dst->leaf()); + } catch (...) { + heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst); + throw; + } + try { + std::uninitialized_copy( + src2->leaf(), src2->leaf() + n2, dst->leaf() + n1); + } catch (...) { + destroy_n(dst->leaf(), n1); + heap::deallocate(node_t::sizeof_leaf_n(n1 + n2), dst); + throw; + } + return dst; + } + + static node_t* copy_leaf_e(edit_t e, + node_t* src1, count_t n1, + node_t* src2, count_t n2) + { + assert(src1->kind() == kind_t::leaf); + assert(src2->kind() == kind_t::leaf); + auto dst = make_leaf_e(e); + try { + std::uninitialized_copy( + src1->leaf(), src1->leaf() + n1, dst->leaf()); + } catch (...) { + heap::deallocate(max_sizeof_leaf, dst); + throw; + } + try { + std::uninitialized_copy( + src2->leaf(), src2->leaf() + n2, dst->leaf() + n1); + } catch (...) { + destroy_n(dst->leaf(), n1); + heap::deallocate(max_sizeof_leaf, dst); + throw; + } + return dst; + } + + static node_t* copy_leaf_e(edit_t e, node_t* src, count_t idx, count_t last) + { + assert(src->kind() == kind_t::leaf); + auto dst = make_leaf_e(e); + try { + std::uninitialized_copy( + src->leaf() + idx, src->leaf() + last, dst->leaf()); + } catch (...) { + heap::deallocate(max_sizeof_leaf, dst); + throw; + } + return dst; + } + + static node_t* copy_leaf(node_t* src, count_t idx, count_t last) + { + assert(src->kind() == kind_t::leaf); + auto dst = make_leaf_n(last - idx); + try { + std::uninitialized_copy( + src->leaf() + idx, src->leaf() + last, dst->leaf()); + } catch (...) { + heap::deallocate(node_t::sizeof_leaf_n(last - idx), dst); + throw; + } + return dst; + } + + template + static node_t* copy_leaf_emplace(node_t* src, count_t n, U&& x) + { + auto dst = copy_leaf_n(n + 1, src, n); + try { + new (dst->leaf() + n) T{std::forward(x)}; + } catch (...) { + destroy_n(dst->leaf(), n); + heap::deallocate(node_t::sizeof_leaf_n(n + 1), dst); + throw; + } + return dst; + } + + static void delete_inner(node_t* p, count_t n) + { + assert(p->kind() == kind_t::inner); + assert(!p->relaxed()); + heap::deallocate(ownee(p).owned() + ? node_t::max_sizeof_inner + : node_t::sizeof_inner_n(n), p); + } + + static void delete_inner_e(node_t* p) + { + assert(p->kind() == kind_t::inner); + assert(!p->relaxed()); + heap::deallocate(node_t::max_sizeof_inner, p); + } + + static void delete_inner_any(node_t* p, count_t n) + { + if (p->relaxed()) + delete_inner_r(p, n); + else + delete_inner(p, n); + } + + static void delete_inner_r(node_t* p, count_t n) + { + assert(p->kind() == kind_t::inner); + auto r = p->relaxed(); + assert(r); + static_if([&] (auto) { + if (node_t::refs(r).dec()) + heap::deallocate(node_t::ownee(r).owned() + ? node_t::max_sizeof_relaxed + : node_t::sizeof_relaxed_n(n), r); + }); + heap::deallocate(ownee(p).owned() + ? node_t::max_sizeof_inner_r + : node_t::sizeof_inner_r_n(n), p); + } + + static void delete_inner_r_e(node_t* p) + { + assert(p->kind() == kind_t::inner); + auto r = p->relaxed(); + assert(r); + static_if([&] (auto) { + if (node_t::refs(r).dec()) + heap::deallocate(node_t::max_sizeof_relaxed, r); + }); + heap::deallocate(node_t::max_sizeof_inner_r, p); + } + + static void delete_leaf(node_t* p, count_t n) + { + assert(p->kind() == kind_t::leaf); + destroy_n(p->leaf(), n); + heap::deallocate(ownee(p).owned() + ? node_t::max_sizeof_leaf + : node_t::sizeof_leaf_n(n), p); + } + + bool can_mutate(edit_t e) const + { + return refs(this).unique() + || ownee(this).can_mutate(e); + } + + bool can_relax() const + { + return !embed_relaxed || relaxed(); + } + + relaxed_t* ensure_mutable_relaxed(edit_t e) + { + auto src_r = relaxed(); + return static_if( + [&] (auto) { return src_r; }, + [&] (auto) { + if (node_t::refs(src_r).unique() || + node_t::ownee(src_r).can_mutate(e)) + return src_r; + else { + if (src_r) + node_t::refs(src_r).dec_unsafe(); + auto dst_r = impl.d.data.inner.relaxed = + new (heap::allocate(max_sizeof_relaxed)) relaxed_t; + node_t::ownee(dst_r) = e; + return dst_r; + } + }); + } + + relaxed_t* ensure_mutable_relaxed_e(edit_t e, edit_t ec) + { + auto src_r = relaxed(); + return static_if( + [&] (auto) { return src_r; }, + [&] (auto) { + if (src_r && (node_t::refs(src_r).unique() || + node_t::ownee(src_r).can_mutate(e))) { + node_t::ownee(src_r) = ec; + return src_r; + } else { + if (src_r) + node_t::refs(src_r).dec_unsafe(); + auto dst_r = impl.d.data.inner.relaxed = + new (heap::allocate(max_sizeof_relaxed)) relaxed_t; + node_t::ownee(dst_r) = ec; + return dst_r; + } + }); + } + + relaxed_t* ensure_mutable_relaxed_n(edit_t e, count_t n) + { + auto src_r = relaxed(); + return static_if( + [&] (auto) { return src_r; }, + [&] (auto) { + if (node_t::refs(src_r).unique() || + node_t::ownee(src_r).can_mutate(e)) + return src_r; + else { + if (src_r) + node_t::refs(src_r).dec_unsafe(); + auto dst_r = + new (heap::allocate(max_sizeof_relaxed)) relaxed_t; + std::copy(src_r->d.sizes, src_r->d.sizes + n, dst_r->d.sizes); + node_t::ownee(dst_r) = e; + return impl.d.data.inner.relaxed = dst_r; + } + }); + } + + node_t* inc() + { + refs(this).inc(); + return this; + } + + const node_t* inc() const + { + refs(this).inc(); + return this; + } + + bool dec() const { return refs(this).dec(); } + void dec_unsafe() const { refs(this).dec_unsafe(); } + + static void inc_nodes(node_t** p, count_t n) + { + for (auto i = p, e = i + n; i != e; ++i) + refs(*i).inc(); + } + +#if IMMER_RBTS_TAGGED_NODE + shift_t compute_shift() + { + if (kind() == kind_t::leaf) + return endshift; + else + return B + inner() [0]->compute_shift(); + } +#endif + + bool check(shift_t shift, size_t size) + { +#if IMMER_DEBUG_DEEP_CHECK + assert(size > 0); + if (shift == endshift) { + assert(kind() == kind_t::leaf); + assert(size <= branches); + } else if (auto r = relaxed()) { + auto count = r->d.count; + assert(count > 0); + assert(count <= branches); + if (r->d.sizes[count - 1] != size) { + IMMER_TRACE_F("check"); + IMMER_TRACE_E(r->d.sizes[count - 1]); + IMMER_TRACE_E(size); + } + assert(r->d.sizes[count - 1] == size); + for (auto i = 1; i < count; ++i) + assert(r->d.sizes[i - 1] < r->d.sizes[i]); + auto last_size = size_t{}; + for (auto i = 0; i < count; ++i) { + assert(inner()[i]->check( + shift - B, + r->d.sizes[i] - last_size)); + last_size = r->d.sizes[i]; + } + } else { + assert(size <= branches << shift); + auto count = (size >> shift) + + (size - ((size >> shift) << shift) > 0); + assert(count <= branches); + if (count) { + for (auto i = 1; i < count - 1; ++i) + assert(inner()[i]->check( + shift - B, + 1 << shift)); + assert(inner()[count - 1]->check( + shift - B, + size - ((count - 1) << shift))); + } + } +#endif // IMMER_DEBUG_DEEP_CHECK + return true; + } +}; + +template +constexpr bits_t derive_bits_leaf_aux() +{ + using node_t = node; + constexpr auto sizeof_elem = sizeof(T); + constexpr auto space = node_t::max_sizeof_inner - node_t::sizeof_packed_leaf_n(0); + constexpr auto full_elems = space / sizeof_elem; + constexpr auto BL = log2(full_elems); + return BL; +} + +template +constexpr bits_t derive_bits_leaf = derive_bits_leaf_aux(); + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/operations.hpp b/src/immer/detail/rbts/operations.hpp new file mode 100644 index 000000000000..e07a3dabfee6 --- /dev/null +++ b/src/immer/detail/rbts/operations.hpp @@ -0,0 +1,2304 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace immer { +namespace detail { +namespace rbts { + +template +struct array_for_visitor +{ + using this_t = array_for_visitor; + + template + friend T* visit_inner(this_t, PosT&& pos, size_t idx) + { return pos.descend(this_t{}, idx); } + + template + friend T* visit_leaf(this_t, PosT&& pos, size_t) + { return pos.node()->leaf(); } +}; + +template +struct region_for_visitor +{ + using this_t = region_for_visitor; + using result_t = std::tuple; + + template + friend result_t visit_inner(this_t, PosT&& pos, size_t idx) + { return pos.towards(this_t{}, idx); } + + template + friend result_t visit_leaf(this_t, PosT&& pos, size_t idx) + { return { pos.node()->leaf(), pos.index(idx), pos.count() }; } +}; + +template +struct get_visitor +{ + using this_t = get_visitor; + + template + friend const T& visit_inner(this_t, PosT&& pos, size_t idx) + { return pos.descend(this_t{}, idx); } + + template + friend const T& visit_leaf(this_t, PosT&& pos, size_t idx) + { return pos.node()->leaf() [pos.index(idx)]; } +}; + +struct for_each_chunk_visitor +{ + using this_t = for_each_chunk_visitor; + + template + friend void visit_inner(this_t, Pos&& pos, Fn&& fn) + { pos.each(this_t{}, fn); } + + template + friend void visit_leaf(this_t, Pos&& pos, Fn&& fn) + { + auto data = pos.node()->leaf(); + fn(data, data + pos.count()); + } +}; + +struct for_each_chunk_p_visitor +{ + using this_t = for_each_chunk_p_visitor; + + template + friend bool visit_inner(this_t, Pos&& pos, Fn&& fn) + { return pos.each_pred(this_t{}, fn); } + + template + friend bool visit_leaf(this_t, Pos&& pos, Fn&& fn) + { + auto data = pos.node()->leaf(); + return fn(data, data + pos.count()); + } +}; + +struct for_each_chunk_left_visitor +{ + using this_t = for_each_chunk_left_visitor; + + template + friend void visit_inner(this_t, Pos&& pos, + size_t last, Fn&& fn) + { + auto l = pos.index(last); + pos.each_left(for_each_chunk_visitor{}, l, fn); + pos.towards_oh(this_t{}, last, l, fn); + } + + template + friend void visit_leaf(this_t, Pos&& pos, + size_t last, + Fn&& fn) + { + auto data = pos.node()->leaf(); + auto l = pos.index(last); + fn(data, data + l + 1); + } +}; + +struct for_each_chunk_right_visitor +{ + using this_t = for_each_chunk_right_visitor; + + template + friend void visit_inner(this_t, Pos&& pos, + size_t first, Fn&& fn) + { + auto f = pos.index(first); + pos.towards_oh(this_t{}, first, f, fn); + pos.each_right(for_each_chunk_visitor{}, f + 1, fn); + } + + template + friend void visit_leaf(this_t, Pos&& pos, + size_t first, + Fn&& fn) + { + auto data = pos.node()->leaf(); + auto f = pos.index(first); + fn(data + f, data + pos.count()); + } +}; + +struct for_each_chunk_i_visitor +{ + using this_t = for_each_chunk_i_visitor; + + template + friend void visit_relaxed(this_t, Pos&& pos, + size_t first, size_t last, + Fn&& fn) + { + // we are going towards *two* indices, so we need to do the + // relaxed as a special case to correct the second index + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + if (f == l) { + auto sbh = pos.size_before(f); + pos.towards_oh_sbh(this_t{}, first, f, sbh, last - sbh, fn); + } else { + assert(f < l); + pos.towards_oh(for_each_chunk_right_visitor{}, first, f, fn); + pos.each_i(for_each_chunk_visitor{}, f + 1, l, fn); + pos.towards_oh(for_each_chunk_left_visitor{}, last - 1, l, fn); + } + } + } + + template + friend void visit_regular(this_t, Pos&& pos, + size_t first, size_t last, + Fn&& fn) + { + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + if (f == l) + pos.towards_oh(this_t{}, first, f, last, fn); + else { + assert(f < l); + pos.towards_oh(for_each_chunk_right_visitor{}, first, f, fn); + pos.each_i(for_each_chunk_visitor{}, f + 1, l, fn); + pos.towards_oh(for_each_chunk_left_visitor{}, last - 1, l, fn); + } + } + } + + template + friend void visit_leaf(this_t, Pos&& pos, + size_t first, size_t last, + Fn&& fn) + { + auto data = pos.node()->leaf(); + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + fn(data + f, data + l + 1); + } + } +}; + +struct for_each_chunk_p_left_visitor +{ + using this_t = for_each_chunk_p_left_visitor; + + template + friend bool visit_inner(this_t, Pos&& pos, + size_t last, Fn&& fn) + { + auto l = pos.index(last); + return pos.each_pred_left(for_each_chunk_p_visitor{}, l, fn) + && pos.towards_oh(this_t{}, last, l, fn); + } + + template + friend bool visit_leaf(this_t, Pos&& pos, + size_t last, + Fn&& fn) + { + auto data = pos.node()->leaf(); + auto l = pos.index(last); + return fn(data, data + l + 1); + } +}; + +struct for_each_chunk_p_right_visitor +{ + using this_t = for_each_chunk_p_right_visitor; + + template + friend bool visit_inner(this_t, Pos&& pos, + size_t first, Fn&& fn) + { + auto f = pos.index(first); + return pos.towards_oh(this_t{}, first, f, fn) + && pos.each_pred_right(for_each_chunk_p_visitor{}, f + 1, fn); + } + + template + friend bool visit_leaf(this_t, Pos&& pos, + size_t first, + Fn&& fn) + { + auto data = pos.node()->leaf(); + auto f = pos.index(first); + return fn(data + f, data + pos.count()); + } +}; + +struct for_each_chunk_p_i_visitor +{ + using this_t = for_each_chunk_p_i_visitor; + + template + friend bool visit_relaxed(this_t, Pos&& pos, + size_t first, size_t last, + Fn&& fn) + { + // we are going towards *two* indices, so we need to do the + // relaxed as a special case to correct the second index + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + if (f == l) { + auto sbh = pos.size_before(f); + return pos.towards_oh_sbh(this_t{}, first, f, sbh, last - sbh, fn); + } else { + assert(f < l); + return pos.towards_oh(for_each_chunk_p_right_visitor{}, first, f, fn) + && pos.each_pred_i(for_each_chunk_p_visitor{}, f + 1, l, fn) + && pos.towards_oh(for_each_chunk_p_left_visitor{}, last - 1, l, fn); + } + } + return true; + } + + template + friend bool visit_regular(this_t, Pos&& pos, + size_t first, size_t last, + Fn&& fn) + { + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + if (f == l) + return pos.towards_oh(this_t{}, first, f, last, fn); + else { + assert(f < l); + return pos.towards_oh(for_each_chunk_p_right_visitor{}, first, f, fn) + && pos.each_pred_i(for_each_chunk_p_visitor{}, f + 1, l, fn) + && pos.towards_oh(for_each_chunk_p_left_visitor{}, last - 1, l, fn); + } + } + return true; + } + + template + friend bool visit_leaf(this_t, Pos&& pos, + size_t first, size_t last, + Fn&& fn) + { + auto data = pos.node()->leaf(); + if (first < last) { + auto f = pos.index(first); + auto l = pos.index(last - 1); + return fn(data + f, data + l + 1); + } + return true; + } +}; + +struct equals_visitor +{ + using this_t = equals_visitor; + + struct this_aux_t + { + template + friend bool visit_inner(this_aux_t, PosR&& posr, + count_t i, PosL&& posl, + Iter&& first, size_t idx) + { return posl.nth_sub(i, this_t{}, posr, first, idx); } + + template + friend bool visit_leaf(this_aux_t, PosR&& posr, + count_t i, PosL&& posl, + Iter&& first, size_t idx) + { return posl.nth_sub_leaf(i, this_t{}, posr, first, idx); } + }; + + struct rrb + { + template + friend bool visit_node(rrb, PosR&& posr, Iter&& first, + Node* rootl, shift_t shiftl, size_t sizel) + { + assert(shiftl <= posr.shift()); + return shiftl == posr.shift() + ? visit_maybe_relaxed_sub(rootl, shiftl, sizel, + this_t{}, posr, first, size_t{}) + : posr.first_sub_inner(rrb{}, first, rootl, shiftl, sizel); + } + }; + + template + static auto equal_chunk_p(Iter&& iter) + { + return [iter] (auto f, auto e) mutable { + if (f == &*iter) { + iter += e - f; + return true; + } + for (; f != e; ++f, ++iter) + if (*f != *iter) + return false; + return true; + }; + } + + template + friend bool visit_relaxed(this_t, PosL&& posl, PosR&& posr, + Iter&& first, size_t idx) + { + auto nl = posl.node(); + auto nr = posr.node(); + if (nl == nr) + return true; + auto cl = posl.count(); + auto cr = posr.count(); + assert(cr > 0); + auto sbr = size_t{}; + auto i = count_t{}; + auto j = count_t{}; + for (; i < cl; ++i) { + auto sbl = posl.size_before(i); + for (; j + 1 < cr && (sbr = posr.size_before(j)) < sbl; ++j); + auto res = sbl == sbr + ? posr.nth_sub(j, this_aux_t{}, i, posl, first, idx + sbl) + : posl.nth_sub(i, for_each_chunk_p_visitor{}, + this_t::equal_chunk_p(first + (idx + sbl))); + if (!res) return false; + } + return true; + } + + template + friend std::enable_if_t, bool> + visit_regular(this_t, PosL&& posl, PosR&& posr, Iter&& first, size_t idx) + { + return visit_relaxed(this_t{}, posl, posr, first, idx); + } + + template + friend std::enable_if_t, bool> + visit_regular(this_t, PosL&& posl, PosR&& posr, Iter&& first, size_t idx) + { + return posl.count() >= posr.count() + ? visit_regular(this_t{}, posl, posr.node()) + : visit_regular(this_t{}, posr, posl.node()); + } + + template + friend bool visit_leaf(this_t, PosL&& posl, + PosR&& posr, Iter&& first, size_t idx) + { + if (posl.node() == posr.node()) + return true; + auto cl = posl.count(); + auto cr = posr.count(); + auto mp = std::min(cl, cr); + return + std::equal(posl.node()->leaf(), + posl.node()->leaf() + mp, + posr.node()->leaf()) && + std::equal(posl.node()->leaf() + mp, + posl.node()->leaf() + posl.count(), + first + (idx + mp)); + } + + template + friend bool visit_regular(this_t, Pos&& pos, NodeT* other) + { + auto node = pos.node(); + return node == other + || pos.each_pred_zip(this_t{}, other); + } + + template + friend bool visit_leaf(this_t, Pos&& pos, NodeT* other) + { + auto node = pos.node(); + return node == other + || std::equal(node->leaf(), node->leaf() + pos.count(), + other->leaf()); + } +}; + +template +struct update_visitor +{ + using node_t = NodeT; + using this_t = update_visitor; + + template + friend node_t* visit_relaxed(this_t, Pos&& pos, size_t idx, Fn&& fn) + { + auto offset = pos.index(idx); + auto count = pos.count(); + auto node = node_t::make_inner_sr_n(count, pos.relaxed()); + try { + auto child = pos.towards_oh(this_t{}, idx, offset, fn); + node_t::do_copy_inner_sr(node, pos.node(), count); + node->inner()[offset]->dec_unsafe(); + node->inner()[offset] = child; + return node; + } catch (...) { + node_t::delete_inner_r(node, count); + throw; + } + } + + template + friend node_t* visit_regular(this_t, Pos&& pos, size_t idx, Fn&& fn) + { + auto offset = pos.index(idx); + auto count = pos.count(); + auto node = node_t::make_inner_n(count); + try { + auto child = pos.towards_oh_ch(this_t{}, idx, offset, count, fn); + node_t::do_copy_inner(node, pos.node(), count); + node->inner()[offset]->dec_unsafe(); + node->inner()[offset] = child; + return node; + } catch (...) { + node_t::delete_inner(node, count); + throw; + } + } + + template + friend node_t* visit_leaf(this_t, Pos&& pos, size_t idx, Fn&& fn) + { + auto offset = pos.index(idx); + auto node = node_t::copy_leaf(pos.node(), pos.count()); + try { + node->leaf()[offset] = std::forward(fn) ( + std::move(node->leaf()[offset])); + return node; + } catch (...) { + node_t::delete_leaf(node, pos.count()); + throw; + } + } +}; + +struct dec_visitor +{ + using this_t = dec_visitor; + + template + friend void visit_relaxed(this_t, Pos&& p) + { + using node_t = node_type; + auto node = p.node(); + if (node->dec()) { + p.each(this_t{}); + node_t::delete_inner_r(node, p.count()); + } + } + + template + friend void visit_regular(this_t, Pos&& p) + { + using node_t = node_type; + auto node = p.node(); + if (node->dec()) { + p.each(this_t{}); + node_t::delete_inner(node, p.count()); + } + } + + template + friend void visit_leaf(this_t, Pos&& p) + { + using node_t = node_type; + auto node = p.node(); + if (node->dec()) { + node_t::delete_leaf(node, p.count()); + } + } +}; + +template +void dec_leaf(NodeT* node, count_t n) +{ + make_leaf_sub_pos(node, n).visit(dec_visitor{}); +} + +template +void dec_inner(NodeT* node, shift_t shift, size_t size) +{ + visit_maybe_relaxed_sub(node, shift, size, dec_visitor()); +} + +template +void dec_relaxed(NodeT* node, shift_t shift) +{ + make_relaxed_pos(node, shift, node->relaxed()).visit(dec_visitor()); +} + +template +void dec_regular(NodeT* node, shift_t shift, size_t size) +{ + make_regular_pos(node, shift, size).visit(dec_visitor()); +} + +template +void dec_empty_regular(NodeT* node) +{ + make_empty_regular_pos(node).visit(dec_visitor()); +} + +template +struct get_mut_visitor +{ + using node_t = NodeT; + using this_t = get_mut_visitor; + using value_t = typename NodeT::value_t; + using edit_t = typename NodeT::edit_t; + + template + friend value_t& visit_relaxed(this_t, Pos&& pos, size_t idx, + edit_t e, node_t** location) + { + auto offset = pos.index(idx); + auto count = pos.count(); + auto node = pos.node(); + if (node->can_mutate(e)) { + return pos.towards_oh(this_t{}, idx, offset, + e, &node->inner()[offset]); + } else { + auto new_node = node_t::copy_inner_sr_e(e, node, count); + try { + auto& res = pos.towards_oh(this_t{}, idx, offset, + e, &new_node->inner()[offset]); + pos.visit(dec_visitor{}); + *location = new_node; + return res; + } catch (...) { + dec_relaxed(new_node, pos.shift()); + throw; + } + } + } + + template + friend value_t& visit_regular(this_t, Pos&& pos, size_t idx, + edit_t e, node_t** location) + { + assert(pos.node() == *location); + auto offset = pos.index(idx); + auto count = pos.count(); + auto node = pos.node(); + if (node->can_mutate(e)) { + return pos.towards_oh_ch(this_t{}, idx, offset, count, + e, &node->inner()[offset]); + } else { + auto new_node = node_t::copy_inner_e(e, node, count); + try { + auto& res = pos.towards_oh_ch(this_t{}, idx, offset, count, + e, &new_node->inner()[offset]); + pos.visit(dec_visitor{}); + *location = new_node; + return res; + } catch (...) { + dec_regular(new_node, pos.shift(), pos.size()); + throw; + } + } + } + + template + friend value_t& visit_leaf(this_t, Pos&& pos, size_t idx, + edit_t e, node_t** location) + { + assert(pos.node() == *location); + auto node = pos.node(); + if (node->can_mutate(e)) { + return node->leaf() [pos.index(idx)]; + } else { + auto new_node = node_t::copy_leaf_e(e, pos.node(), pos.count()); + pos.visit(dec_visitor{}); + *location = new_node; + return new_node->leaf() [pos.index(idx)]; + } + } +}; + +template +struct push_tail_mut_visitor +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using this_t = push_tail_mut_visitor; + using this_no_mut_t = push_tail_mut_visitor; + using node_t = NodeT; + using edit_t = typename NodeT::edit_t; + + template + friend node_t* visit_relaxed(this_t, Pos&& pos, edit_t e, node_t* tail, count_t ts) + { + auto node = pos.node(); + auto level = pos.shift(); + auto idx = pos.count() - 1; + auto children = pos.size(idx); + auto new_idx = children == size_t{1} << level || level == BL + ? idx + 1 : idx; + auto new_child = static_cast(nullptr); + auto mutate = Mutating && node->can_mutate(e); + + if (new_idx >= branches) + return nullptr; + else if (idx == new_idx) { + new_child = mutate + ? pos.last_oh_csh(this_t{}, idx, children, e, tail, ts) + : pos.last_oh_csh(this_no_mut_t{}, idx, children, e, tail, ts); + if (!new_child) { + if (++new_idx < branches) + new_child = node_t::make_path_e(e, level - B, tail); + else + return nullptr; + } + } else + new_child = node_t::make_path_e(e, level - B, tail); + + if (mutate) { + auto count = new_idx + 1; + auto relaxed = node->ensure_mutable_relaxed_n(e, new_idx); + node->inner()[new_idx] = new_child; + relaxed->d.sizes[new_idx] = pos.size() + ts; + relaxed->d.count = count; + return node; + } else { + try { + auto count = new_idx + 1; + auto new_node = node_t::copy_inner_r_e(e, pos.node(), new_idx); + auto relaxed = new_node->relaxed(); + new_node->inner()[new_idx] = new_child; + relaxed->d.sizes[new_idx] = pos.size() + ts; + relaxed->d.count = count; + if (Mutating) pos.visit(dec_visitor{}); + return new_node; + } catch (...) { + auto shift = pos.shift(); + auto size = new_idx == idx ? children + ts : ts; + if (shift > BL) { + tail->inc(); + dec_inner(new_child, shift - B, size); + } + throw; + } + } + } + + template + friend node_t* visit_regular(this_t, Pos&& pos, edit_t e, node_t* tail, Args&&...) + { + assert((pos.size() & mask) == 0); + auto node = pos.node(); + auto idx = pos.index(pos.size() - 1); + auto new_idx = pos.index(pos.size() + branches - 1); + auto mutate = Mutating && node->can_mutate(e); + if (mutate) { + node->inner()[new_idx] = + idx == new_idx ? pos.last_oh(this_t{}, idx, e, tail) + /* otherwise */ : node_t::make_path_e(e, pos.shift() - B, tail); + return node; + } else { + auto new_parent = node_t::make_inner_e(e); + try { + new_parent->inner()[new_idx] = + idx == new_idx ? pos.last_oh(this_no_mut_t{}, idx, e, tail) + /* otherwise */ : node_t::make_path_e(e, pos.shift() - B, tail); + node_t::do_copy_inner(new_parent, node, new_idx); + if (Mutating) pos.visit(dec_visitor{}); + return new_parent; + } catch (...) { + node_t::delete_inner_e(new_parent); + throw; + } + } + } + + template + friend node_t* visit_leaf(this_t, Pos&& pos, edit_t e, node_t* tail, Args&&...) + { IMMER_UNREACHABLE; } +}; + +template +struct push_tail_visitor +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using this_t = push_tail_visitor; + using node_t = NodeT; + + template + friend node_t* visit_relaxed(this_t, Pos&& pos, node_t* tail, count_t ts) + { + auto level = pos.shift(); + auto idx = pos.count() - 1; + auto children = pos.size(idx); + auto new_idx = children == size_t{1} << level || level == BL + ? idx + 1 : idx; + auto new_child = static_cast(nullptr); + if (new_idx >= branches) + return nullptr; + else if (idx == new_idx) { + new_child = pos.last_oh_csh(this_t{}, idx, children, tail, ts); + if (!new_child) { + if (++new_idx < branches) + new_child = node_t::make_path(level - B, tail); + else + return nullptr; + } + } else + new_child = node_t::make_path(level - B, tail); + try { + auto count = new_idx + 1; + auto new_parent = node_t::copy_inner_r_n(count, pos.node(), new_idx); + auto new_relaxed = new_parent->relaxed(); + new_parent->inner()[new_idx] = new_child; + new_relaxed->d.sizes[new_idx] = pos.size() + ts; + new_relaxed->d.count = count; + return new_parent; + } catch (...) { + auto shift = pos.shift(); + auto size = new_idx == idx ? children + ts : ts; + if (shift > BL) { + tail->inc(); + dec_inner(new_child, shift - B, size); + } + throw; + } + } + + template + friend node_t* visit_regular(this_t, Pos&& pos, node_t* tail, Args&&...) + { + assert((pos.size() & mask) == 0); + auto idx = pos.index(pos.size() - 1); + auto new_idx = pos.index(pos.size() + branches - 1); + auto count = new_idx + 1; + auto new_parent = node_t::make_inner_n(count); + try { + new_parent->inner()[new_idx] = + idx == new_idx ? pos.last_oh(this_t{}, idx, tail) + /* otherwise */ : node_t::make_path(pos.shift() - B, tail); + } catch (...) { + node_t::delete_inner(new_parent, count); + throw; + } + return node_t::do_copy_inner(new_parent, pos.node(), new_idx); + } + + template + friend node_t* visit_leaf(this_t, Pos&& pos, node_t* tail, Args&&...) + { IMMER_UNREACHABLE; } +}; + +struct dec_right_visitor +{ + using this_t = dec_right_visitor; + using dec_t = dec_visitor; + + template + friend void visit_relaxed(this_t, Pos&& p, count_t idx) + { + using node_t = node_type; + auto node = p.node(); + if (node->dec()) { + p.each_right(dec_t{}, idx); + node_t::delete_inner_r(node, p.count()); + } + } + + template + friend void visit_regular(this_t, Pos&& p, count_t idx) + { + using node_t = node_type; + auto node = p.node(); + if (node->dec()) { + p.each_right(dec_t{}, idx); + node_t::delete_inner(node, p.count()); + } + } + + template + friend void visit_leaf(this_t, Pos&& p, count_t idx) + { IMMER_UNREACHABLE; } +}; + +template +struct slice_right_mut_visitor +{ + using node_t = NodeT; + using this_t = slice_right_mut_visitor; + using edit_t = typename NodeT::edit_t; + + // returns a new shift, new root, the new tail size and the new tail + using result_t = std::tuple; + using no_collapse_t = slice_right_mut_visitor; + using no_collapse_no_mut_t = slice_right_mut_visitor; + using no_mut_t = slice_right_mut_visitor; + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + template + friend result_t visit_relaxed(this_t, PosT&& pos, size_t last, edit_t e) + { + auto idx = pos.index(last); + auto node = pos.node(); + auto mutate = Mutating && node->can_mutate(e); + if (Collapse && idx == 0) { + auto res = mutate + ? pos.towards_oh(this_t{}, last, idx, e) + : pos.towards_oh(no_mut_t{}, last, idx, e); + if (Mutating) pos.visit(dec_right_visitor{}, count_t{1}); + return res; + } else { + using std::get; + auto subs = mutate + ? pos.towards_oh(no_collapse_t{}, last, idx, e) + : pos.towards_oh(no_collapse_no_mut_t{}, last, idx, e); + auto next = get<1>(subs); + auto ts = get<2>(subs); + auto tail = get<3>(subs); + try { + if (next) { + if (mutate) { + auto nodr = node->ensure_mutable_relaxed_n(e, idx); + pos.each_right(dec_visitor{}, idx + 1); + node->inner()[idx] = next; + nodr->d.sizes[idx] = last + 1 - ts; + nodr->d.count = idx + 1; + return { pos.shift(), node, ts, tail }; + } else { + auto newn = node_t::copy_inner_r_e(e, node, idx); + auto newr = newn->relaxed(); + newn->inner()[idx] = next; + newr->d.sizes[idx] = last + 1 - ts; + newr->d.count = idx + 1; + if (Mutating) pos.visit(dec_visitor{}); + return { pos.shift(), newn, ts, tail }; + } + } else if (idx == 0) { + if (Mutating) pos.visit(dec_right_visitor{}, count_t{1}); + return { pos.shift(), nullptr, ts, tail }; + } else if (Collapse && idx == 1 && pos.shift() > BL) { + auto newn = pos.node()->inner()[0]; + if (!mutate) newn->inc(); + if (Mutating) pos.visit(dec_right_visitor{}, count_t{2}); + return { pos.shift() - B, newn, ts, tail }; + } else { + if (mutate) { + pos.each_right(dec_visitor{}, idx + 1); + node->ensure_mutable_relaxed_n(e, idx)->d.count = idx; + return { pos.shift(), node, ts, tail }; + } else { + auto newn = node_t::copy_inner_r_e(e, node, idx); + if (Mutating) pos.visit(dec_visitor{}); + return { pos.shift(), newn, ts, tail }; + } + } + } catch (...) { + assert(!mutate); + assert(!next || pos.shift() > BL); + if (next) + dec_inner(next, pos.shift() - B, + last + 1 - ts - pos.size_before(idx)); + dec_leaf(tail, ts); + throw; + } + } + } + + template + friend result_t visit_regular(this_t, PosT&& pos, size_t last, edit_t e) + { + auto idx = pos.index(last); + auto node = pos.node(); + auto mutate = Mutating && node->can_mutate(e); + if (Collapse && idx == 0) { + auto res = mutate + ? pos.towards_oh(this_t{}, last, idx, e) + : pos.towards_oh(no_mut_t{}, last, idx, e); + if (Mutating) pos.visit(dec_right_visitor{}, count_t{1}); + return res; + } else { + using std::get; + auto subs = mutate + ? pos.towards_oh(no_collapse_t{}, last, idx, e) + : pos.towards_oh(no_collapse_no_mut_t{}, last, idx, e); + auto next = get<1>(subs); + auto ts = get<2>(subs); + auto tail = get<3>(subs); + try { + if (next) { + if (mutate) { + node->inner()[idx] = next; + pos.each_right(dec_visitor{}, idx + 1); + return { pos.shift(), node, ts, tail }; + } else { + auto newn = node_t::copy_inner_e(e, node, idx); + newn->inner()[idx] = next; + if (Mutating) pos.visit(dec_visitor{}); + return { pos.shift(), newn, ts, tail }; + } + } else if (idx == 0) { + if (Mutating) pos.visit(dec_right_visitor{}, count_t{1}); + return { pos.shift(), nullptr, ts, tail }; + } else if (Collapse && idx == 1 && pos.shift() > BL) { + auto newn = pos.node()->inner()[0]; + if (!mutate) newn->inc(); + if (Mutating) pos.visit(dec_right_visitor{}, count_t{2}); + return { pos.shift() - B, newn, ts, tail }; + } else { + if (mutate) { + pos.each_right(dec_visitor{}, idx + 1); + return { pos.shift(), node, ts, tail }; + } else { + auto newn = node_t::copy_inner_e(e, node, idx); + if (Mutating) pos.visit(dec_visitor{}); + return { pos.shift(), newn, ts, tail }; + } + } + } catch (...) { + assert(!mutate); + assert(!next || pos.shift() > BL); + assert(tail); + if (next) dec_regular(next, pos.shift() - B, last + 1 - ts); + dec_leaf(tail, ts); + throw; + } + } + } + + template + friend result_t visit_leaf(this_t, PosT&& pos, size_t last, edit_t e) + { + auto old_tail_size = pos.count(); + auto new_tail_size = pos.index(last) + 1; + auto node = pos.node(); + auto mutate = Mutating && node->can_mutate(e); + if (new_tail_size == old_tail_size) { + if (!Mutating) node->inc(); + return { 0, nullptr, new_tail_size, node }; + } else if (mutate) { + destroy_n(node->leaf() + new_tail_size, + old_tail_size - new_tail_size); + return { 0, nullptr, new_tail_size, node }; + } else { + auto new_tail = node_t::copy_leaf_e(e, node, new_tail_size); + if (Mutating) pos.visit(dec_visitor{}); + return { 0, nullptr, new_tail_size, new_tail }; + } + } +}; + +template +struct slice_right_visitor +{ + using node_t = NodeT; + using this_t = slice_right_visitor; + + // returns a new shift, new root, the new tail size and the new tail + using result_t = std::tuple; + using no_collapse_t = slice_right_visitor; + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + template + friend result_t visit_relaxed(this_t, PosT&& pos, size_t last) + { + auto idx = pos.index(last); + if (Collapse && idx == 0) { + return pos.towards_oh(this_t{}, last, idx); + } else { + using std::get; + auto subs = pos.towards_oh(no_collapse_t{}, last, idx); + auto next = get<1>(subs); + auto ts = get<2>(subs); + auto tail = get<3>(subs); + try { + if (next) { + auto count = idx + 1; + auto newn = node_t::copy_inner_r_n(count, pos.node(), idx); + auto newr = newn->relaxed(); + newn->inner()[idx] = next; + newr->d.sizes[idx] = last + 1 - ts; + newr->d.count = count; + return { pos.shift(), newn, ts, tail }; + } else if (idx == 0) { + return { pos.shift(), nullptr, ts, tail }; + } else if (Collapse && idx == 1 && pos.shift() > BL) { + auto newn = pos.node()->inner()[0]; + return { pos.shift() - B, newn->inc(), ts, tail }; + } else { + auto newn = node_t::copy_inner_r(pos.node(), idx); + return { pos.shift(), newn, ts, tail }; + } + } catch (...) { + assert(!next || pos.shift() > BL); + if (next) dec_inner(next, pos.shift() - B, + last + 1 - ts - pos.size_before(idx)); + if (tail) dec_leaf(tail, ts); + throw; + } + } + } + + template + friend result_t visit_regular(this_t, PosT&& pos, size_t last) + { + auto idx = pos.index(last); + if (Collapse && idx == 0) { + return pos.towards_oh(this_t{}, last, idx); + } else { + using std::get; + auto subs = pos.towards_oh(no_collapse_t{}, last, idx); + auto next = get<1>(subs); + auto ts = get<2>(subs); + auto tail = get<3>(subs); + try { + if (next) { + auto newn = node_t::copy_inner_n(idx + 1, pos.node(), idx); + newn->inner()[idx] = next; + return { pos.shift(), newn, ts, tail }; + } else if (idx == 0) { + return { pos.shift(), nullptr, ts, tail }; + } else if (Collapse && idx == 1 && pos.shift() > BL) { + auto newn = pos.node()->inner()[0]; + return { pos.shift() - B, newn->inc(), ts, tail }; + } else { + auto newn = node_t::copy_inner_n(idx, pos.node(), idx); + return { pos.shift(), newn, ts, tail }; + } + } catch (...) { + assert(!next || pos.shift() > BL); + assert(tail); + if (next) dec_regular(next, pos.shift() - B, last + 1 - ts); + dec_leaf(tail, ts); + throw; + } + } + } + + template + friend result_t visit_leaf(this_t, PosT&& pos, size_t last) + { + auto old_tail_size = pos.count(); + auto new_tail_size = pos.index(last) + 1; + auto new_tail = new_tail_size == old_tail_size + ? pos.node()->inc() + : node_t::copy_leaf(pos.node(), new_tail_size); + return { 0, nullptr, new_tail_size, new_tail }; + } +}; + +struct dec_left_visitor +{ + using this_t = dec_left_visitor; + using dec_t = dec_visitor; + + template + friend void visit_relaxed(this_t, Pos&& p, count_t idx) + { + using node_t = node_type; + auto node = p.node(); + if (node->dec()) { + p.each_left(dec_t{}, idx); + node_t::delete_inner_r(node, p.count()); + } + } + + template + friend void visit_regular(this_t, Pos&& p, count_t idx) + { + using node_t = node_type; + auto node = p.node(); + if (node->dec()) { + p.each_left(dec_t{}, idx); + node_t::delete_inner(node, p.count()); + } + } + + template + friend void visit_leaf(this_t, Pos&& p, count_t idx) + { IMMER_UNREACHABLE; } +}; + +template +struct slice_left_mut_visitor +{ + using node_t = NodeT; + using this_t = slice_left_mut_visitor; + using edit_t = typename NodeT::edit_t; + using value_t = typename NodeT::value_t; + using relaxed_t = typename NodeT::relaxed_t; + // returns a new shift and new root + using result_t = std::tuple; + + using no_collapse_t = slice_left_mut_visitor; + using no_collapse_no_mut_t = slice_left_mut_visitor; + using no_mut_t = slice_left_mut_visitor; + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + template + friend result_t visit_relaxed(this_t, PosT&& pos, size_t first, edit_t e) + { + auto idx = pos.subindex(first); + auto count = pos.count(); + auto node = pos.node(); + auto mutate = Mutating && node->can_mutate(e); + auto left_size = pos.size_before(idx); + auto child_size = pos.size_sbh(idx, left_size); + auto dropped_size = first; + auto child_dropped_size = dropped_size - left_size; + if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { + auto r = mutate + ? pos.towards_sub_oh(this_t{}, first, idx, e) + : pos.towards_sub_oh(no_mut_t{}, first, idx, e); + if (Mutating) pos.visit(dec_left_visitor{}, idx); + return r; + } else { + using std::get; + auto newn = mutate + ? (node->ensure_mutable_relaxed(e), node) + : node_t::make_inner_r_e(e); + auto newr = newn->relaxed(); + auto newcount = count - idx; + auto new_child_size = child_size - child_dropped_size; + try { + auto subs = mutate + ? pos.towards_sub_oh(no_collapse_t{}, first, idx, e) + : pos.towards_sub_oh(no_collapse_no_mut_t{}, first, idx, e); + if (mutate) pos.each_left(dec_visitor{}, idx); + pos.copy_sizes(idx + 1, newcount - 1, + new_child_size, newr->d.sizes + 1); + std::uninitialized_copy(node->inner() + idx + 1, + node->inner() + count, + newn->inner() + 1); + newn->inner()[0] = get<1>(subs); + newr->d.sizes[0] = new_child_size; + newr->d.count = newcount; + if (!mutate) { + node_t::inc_nodes(newn->inner() + 1, newcount - 1); + if (Mutating) pos.visit(dec_visitor{}); + } + return { pos.shift(), newn }; + } catch (...) { + if (!mutate) node_t::delete_inner_r_e(newn); + throw; + } + } + } + + template + friend result_t visit_regular(this_t, PosT&& pos, size_t first, edit_t e) + { + auto idx = pos.subindex(first); + auto count = pos.count(); + auto node = pos.node(); + auto mutate = Mutating + // this is more restrictive than actually needed because + // it causes the algorithm to also avoid mutating the leaf + // in place + && !node_t::embed_relaxed + && node->can_mutate(e); + auto left_size = pos.size_before(idx); + auto child_size = pos.size_sbh(idx, left_size); + auto dropped_size = first; + auto child_dropped_size = dropped_size - left_size; + if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { + auto r = mutate + ? pos.towards_sub_oh(this_t{}, first, idx, e) + : pos.towards_sub_oh(no_mut_t{}, first, idx, e); + if (Mutating) pos.visit(dec_left_visitor{}, idx); + return r; + } else { + using std::get; + // if possible, we convert the node to a relaxed one + // simply by allocating a `relaxed_t` size table for + // it... maybe some of this magic should be moved as a + // `node<...>` static method... + auto newcount = count - idx; + auto newn = mutate + ? (node->impl.d.data.inner.relaxed = new ( + node_t::heap::allocate( + node_t::max_sizeof_relaxed, + norefs_tag{})) relaxed_t, + node) + : node_t::make_inner_r_e(e); + auto newr = newn->relaxed(); + try { + auto subs = mutate + ? pos.towards_sub_oh(no_collapse_t{}, first, idx, e) + : pos.towards_sub_oh(no_collapse_no_mut_t{}, first, idx, e); + if (mutate) pos.each_left(dec_visitor{}, idx); + newr->d.sizes[0] = child_size - child_dropped_size; + pos.copy_sizes(idx + 1, newcount - 1, + newr->d.sizes[0], newr->d.sizes + 1); + newr->d.count = newcount; + newn->inner()[0] = get<1>(subs); + std::uninitialized_copy(node->inner() + idx + 1, + node->inner() + count, + newn->inner() + 1); + if (!mutate) { + node_t::inc_nodes(newn->inner() + 1, newcount - 1); + if (Mutating) pos.visit(dec_visitor{}); + } + return { pos.shift(), newn }; + } catch (...) { + if (!mutate) node_t::delete_inner_r_e(newn); + else { + // restore the regular node that we were + // attempting to relax... + node_t::heap::deallocate(node_t::max_sizeof_relaxed, + node->impl.d.data.inner.relaxed); + node->impl.d.data.inner.relaxed = nullptr; + } + throw; + } + } + } + + template + friend result_t visit_leaf(this_t, PosT&& pos, size_t first, edit_t e) + { + auto node = pos.node(); + auto idx = pos.index(first); + auto count = pos.count(); + auto mutate = Mutating + && std::is_nothrow_move_constructible::value + && node->can_mutate(e); + if (mutate) { + auto data = node->leaf(); + auto newcount = count - idx; + std::move(data + idx, data + count, data); + destroy_n(data + newcount, idx); + return { 0, node }; + } else { + auto newn = node_t::copy_leaf_e(e, node, idx, count); + if (Mutating) pos.visit(dec_visitor{}); + return { 0, newn }; + } + } +}; + +template +struct slice_left_visitor +{ + using node_t = NodeT; + using this_t = slice_left_visitor; + + // returns a new shift and new root + using result_t = std::tuple; + using no_collapse_t = slice_left_visitor; + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + template + friend result_t visit_inner(this_t, PosT&& pos, size_t first) + { + auto idx = pos.subindex(first); + auto count = pos.count(); + auto left_size = pos.size_before(idx); + auto child_size = pos.size_sbh(idx, left_size); + auto dropped_size = first; + auto child_dropped_size = dropped_size - left_size; + if (Collapse && pos.shift() > BL && idx == pos.count() - 1) { + return pos.towards_sub_oh(this_t{}, first, idx); + } else { + using std::get; + auto n = pos.node(); + auto newc = count - idx; + auto newn = node_t::make_inner_r_n(newc); + try { + auto subs = pos.towards_sub_oh(no_collapse_t{}, first, idx); + auto newr = newn->relaxed(); + newr->d.count = count - idx; + newr->d.sizes[0] = child_size - child_dropped_size; + pos.copy_sizes(idx + 1, newr->d.count - 1, + newr->d.sizes[0], newr->d.sizes + 1); + assert(newr->d.sizes[newr->d.count - 1] == pos.size() - dropped_size); + newn->inner()[0] = get<1>(subs); + std::uninitialized_copy(n->inner() + idx + 1, + n->inner() + count, + newn->inner() + 1); + node_t::inc_nodes(newn->inner() + 1, newr->d.count - 1); + return { pos.shift(), newn }; + } catch (...) { + node_t::delete_inner_r(newn, newc); + throw; + } + } + } + + template + friend result_t visit_leaf(this_t, PosT&& pos, size_t first) + { + auto n = node_t::copy_leaf(pos.node(), pos.index(first), pos.count()); + return { 0, n }; + } +}; + +template +struct concat_center_pos +{ + static constexpr auto B = Node::bits; + static constexpr auto BL = Node::bits_leaf; + + static constexpr count_t max_children = 3; + + using node_t = Node; + using edit_t = typename Node::edit_t; + + shift_t shift_ = 0u; + count_t count_ = 0u; + node_t* nodes_[max_children]; + size_t sizes_[max_children]; + + auto shift() const { return shift_; } + + concat_center_pos(shift_t s, + Node* n0, size_t s0) + : shift_{s}, count_{1}, nodes_{n0}, sizes_{s0} {} + + concat_center_pos(shift_t s, + Node* n0, size_t s0, + Node* n1, size_t s1) + : shift_{s}, count_{2}, nodes_{n0, n1}, sizes_{s0, s1} {} + + concat_center_pos(shift_t s, + Node* n0, size_t s0, + Node* n1, size_t s1, + Node* n2, size_t s2) + : shift_{s}, count_{3}, nodes_{n0, n1, n2}, sizes_{s0, s1, s2} {} + + template + void each_sub(Visitor v, Args&& ...args) + { + if (shift_ == BL) { + for (auto i = count_t{0}; i < count_; ++i) + make_leaf_sub_pos(nodes_[i], sizes_[i]).visit(v, args...); + } else { + for (auto i = count_t{0}; i < count_; ++i) + make_relaxed_pos(nodes_[i], shift_ - B, nodes_[i]->relaxed()).visit(v, args...); + } + } + + relaxed_pos realize() && + { + if (count_ > 1) { + try { + auto result = node_t::make_inner_r_n(count_); + auto r = result->relaxed(); + r->d.count = count_; + std::copy(nodes_, nodes_ + count_, result->inner()); + std::copy(sizes_, sizes_ + count_, r->d.sizes); + return { result, shift_, r }; + } catch (...) { + each_sub(dec_visitor{}); + throw; + } + } else { + assert(shift_ >= B + BL); + return { nodes_[0], shift_ - B, nodes_[0]->relaxed() }; + } + } + + relaxed_pos realize_e(edit_t e) + { + if (count_ > 1) { + auto result = node_t::make_inner_r_e(e); + auto r = result->relaxed(); + r->d.count = count_; + std::copy(nodes_, nodes_ + count_, result->inner()); + std::copy(sizes_, sizes_ + count_, r->d.sizes); + return { result, shift_, r }; + } else { + assert(shift_ >= B + BL); + return { nodes_[0], shift_ - B, nodes_[0]->relaxed() }; + } + } +}; + +template +struct concat_merger +{ + using node_t = Node; + static constexpr auto B = Node::bits; + static constexpr auto BL = Node::bits_leaf; + + using result_t = concat_center_pos; + + count_t* curr_; + count_t n_; + result_t result_; + + concat_merger(shift_t shift, count_t* counts, count_t n) + : curr_{counts} + , n_{n} + , result_{shift + B, node_t::make_inner_r_n(std::min(n_, branches)), 0} + {} + + node_t* to_ = {}; + count_t to_offset_ = {}; + size_t to_size_ = {}; + + void add_child(node_t* p, size_t size) + { + ++curr_; + auto parent = result_.nodes_[result_.count_ - 1]; + auto relaxed = parent->relaxed(); + if (relaxed->d.count == branches) { + assert(result_.count_ < result_t::max_children); + n_ -= branches; + parent = node_t::make_inner_r_n(std::min(n_, branches)); + relaxed = parent->relaxed(); + result_.nodes_[result_.count_] = parent; + result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1]; + ++result_.count_; + } + auto idx = relaxed->d.count++; + result_.sizes_[result_.count_ - 1] += size; + relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0); + parent->inner() [idx] = p; + }; + + template + void merge_leaf(Pos&& p) + { + auto from = p.node(); + auto from_size = p.size(); + auto from_count = p.count(); + assert(from_size); + if (!to_ && *curr_ == from_count) { + add_child(from, from_size); + from->inc(); + } else { + auto from_offset = count_t{}; + auto from_data = from->leaf(); + do { + if (!to_) { + to_ = node_t::make_leaf_n(*curr_); + to_offset_ = 0; + } + auto data = to_->leaf(); + auto to_copy = std::min(from_count - from_offset, + *curr_ - to_offset_); + std::uninitialized_copy(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + to_offset_ += to_copy; + from_offset += to_copy; + if (*curr_ == to_offset_) { + add_child(to_, to_offset_); + to_ = nullptr; + } + } while (from_offset != from_count); + } + } + + template + void merge_inner(Pos&& p) + { + auto from = p.node(); + auto from_size = p.size(); + auto from_count = p.count(); + assert(from_size); + if (!to_ && *curr_ == from_count) { + add_child(from, from_size); + from->inc(); + } else { + auto from_offset = count_t{}; + auto from_data = from->inner(); + do { + if (!to_) { + to_ = node_t::make_inner_r_n(*curr_); + to_offset_ = 0; + to_size_ = 0; + } + auto data = to_->inner(); + auto to_copy = std::min(from_count - from_offset, + *curr_ - to_offset_); + std::uninitialized_copy(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + node_t::inc_nodes(from_data + from_offset, to_copy); + auto sizes = to_->relaxed()->d.sizes; + p.copy_sizes(from_offset, to_copy, + to_size_, sizes + to_offset_); + to_offset_ += to_copy; + from_offset += to_copy; + to_size_ = sizes[to_offset_ - 1]; + if (*curr_ == to_offset_) { + to_->relaxed()->d.count = to_offset_; + add_child(to_, to_size_); + to_ = nullptr; + } + } while (from_offset != from_count); + } + } + + concat_center_pos finish() const + { + assert(!to_); + return result_; + } + + void abort() + { + auto shift = result_.shift_ - B; + if (to_) { + if (shift == BL) + node_t::delete_leaf(to_, to_offset_); + else { + to_->relaxed()->d.count = to_offset_; + dec_relaxed(to_, shift - B); + } + } + result_.each_sub(dec_visitor()); + } +}; + +struct concat_merger_visitor +{ + using this_t = concat_merger_visitor; + + template + friend void visit_inner(this_t, Pos&& p, Merger& merger) + { merger.merge_inner(p); } + + template + friend void visit_leaf(this_t, Pos&& p, Merger& merger) + { merger.merge_leaf(p); } +}; + +struct concat_rebalance_plan_fill_visitor +{ + using this_t = concat_rebalance_plan_fill_visitor; + + template + friend void visit_node(this_t, Pos&& p, Plan& plan) + { + auto count = p.count(); + assert(plan.n < Plan::max_children); + plan.counts[plan.n++] = count; + plan.total += count; + } +}; + +template +struct concat_rebalance_plan +{ + static constexpr auto max_children = 2 * branches + 1; + + count_t counts [max_children]; + count_t n = 0u; + count_t total = 0u; + + template + void fill(LPos&& lpos, CPos&& cpos, RPos&& rpos) + { + assert(n == 0u); + assert(total == 0u); + using visitor_t = concat_rebalance_plan_fill_visitor; + lpos.each_left_sub(visitor_t{}, *this); + cpos.each_sub(visitor_t{}, *this); + rpos.each_right_sub(visitor_t{}, *this); + } + + void shuffle(shift_t shift) + { + // gcc seems to not really understand this code... :( +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + constexpr count_t rrb_extras = 2; + constexpr count_t rrb_invariant = 1; + const auto bits = shift == BL ? BL : B; + const auto branches = count_t{1} << bits; + const auto optimal = ((total - 1) >> bits) + 1; + count_t i = 0; + while (n >= optimal + rrb_extras) { + // skip ok nodes + while (counts[i] > branches - rrb_invariant) i++; + // short node, redistribute + auto remaining = counts[i]; + do { + auto count = std::min(remaining + counts[i+1], branches); + counts[i] = count; + remaining += counts[i + 1] - count; + ++i; + } while (remaining > 0); + // remove node + std::move(counts + i + 1, counts + n, counts + i); + --n; + --i; + } +#pragma GCC diagnostic pop + } + + template + concat_center_pos> + merge(LPos&& lpos, CPos&& cpos, RPos&& rpos) + { + using node_t = node_type; + using merger_t = concat_merger; + using visitor_t = concat_merger_visitor; + auto merger = merger_t{cpos.shift(), counts, n}; + try { + lpos.each_left_sub(visitor_t{}, merger); + cpos.each_sub(visitor_t{}, merger); + rpos.each_right_sub(visitor_t{}, merger); + cpos.each_sub(dec_visitor{}); + return merger.finish(); + } catch (...) { + merger.abort(); + throw; + } + } +}; + +template +concat_center_pos +concat_rebalance(LPos&& lpos, CPos&& cpos, RPos&& rpos) +{ + auto plan = concat_rebalance_plan{}; + plan.fill(lpos, cpos, rpos); + plan.shuffle(cpos.shift()); + try { + return plan.merge(lpos, cpos, rpos); + } catch (...) { + cpos.each_sub(dec_visitor{}); + throw; + } +} + +template +concat_center_pos +concat_leafs(LPos&& lpos, TPos&& tpos, RPos&& rpos) +{ + static_assert(Node::bits >= 2, ""); + assert(lpos.shift() == tpos.shift()); + assert(lpos.shift() == rpos.shift()); + assert(lpos.shift() == 0); + if (tpos.count() > 0) + return { + Node::bits_leaf, + lpos.node()->inc(), lpos.count(), + tpos.node()->inc(), tpos.count(), + rpos.node()->inc(), rpos.count(), + }; + else + return { + Node::bits_leaf, + lpos.node()->inc(), lpos.count(), + rpos.node()->inc(), rpos.count(), + }; +} + +template +struct concat_left_visitor; +template +struct concat_right_visitor; +template +struct concat_both_visitor; + +template +concat_center_pos +concat_inners(LPos&& lpos, TPos&& tpos, RPos&& rpos) +{ + auto lshift = lpos.shift(); + auto rshift = rpos.shift(); + if (lshift > rshift) { + auto cpos = lpos.last_sub(concat_left_visitor{}, tpos, rpos); + return concat_rebalance(lpos, cpos, null_sub_pos{}); + } else if (lshift < rshift) { + auto cpos = rpos.first_sub(concat_right_visitor{}, lpos, tpos); + return concat_rebalance(null_sub_pos{}, cpos, rpos); + } else { + assert(lshift == rshift); + assert(Node::bits_leaf == 0u || lshift > 0); + auto cpos = lpos.last_sub(concat_both_visitor{}, tpos, rpos); + return concat_rebalance(lpos, cpos, rpos); + } +} + +template +struct concat_left_visitor +{ + using this_t = concat_left_visitor; + + template + friend concat_center_pos + visit_inner(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos) + { return concat_inners(lpos, tpos, rpos); } + + template + friend concat_center_pos + visit_leaf(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos) + { IMMER_UNREACHABLE; } +}; + +template +struct concat_right_visitor +{ + using this_t = concat_right_visitor; + + template + friend concat_center_pos + visit_inner(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos) + { return concat_inners(lpos, tpos, rpos); } + + template + friend concat_center_pos + visit_leaf(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos) + { return concat_leafs(lpos, tpos, rpos); } +}; + +template +struct concat_both_visitor +{ + using this_t = concat_both_visitor; + + template + friend concat_center_pos + visit_inner(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos) + { return rpos.first_sub(concat_right_visitor{}, lpos, tpos); } + + template + friend concat_center_pos + visit_leaf(this_t, LPos&& lpos, TPos&& tpos, RPos&& rpos) + { return rpos.first_sub_leaf(concat_right_visitor{}, lpos, tpos); } +}; + +template +struct concat_trees_right_visitor +{ + using this_t = concat_trees_right_visitor; + + template + friend concat_center_pos + visit_node(this_t, RPos&& rpos, LPos&& lpos, TPos&& tpos) + { return concat_inners(lpos, tpos, rpos); } +}; + +template +struct concat_trees_left_visitor +{ + using this_t = concat_trees_left_visitor; + + template + friend concat_center_pos + visit_node(this_t, LPos&& lpos, TPos&& tpos, Args&& ...args) + { return visit_maybe_relaxed_sub( + args..., + concat_trees_right_visitor{}, + lpos, tpos); } +}; + +template +relaxed_pos +concat_trees(Node* lroot, shift_t lshift, size_t lsize, + Node* ltail, count_t ltcount, + Node* rroot, shift_t rshift, size_t rsize) +{ + return visit_maybe_relaxed_sub( + lroot, lshift, lsize, + concat_trees_left_visitor{}, + make_leaf_pos(ltail, ltcount), + rroot, rshift, rsize) + .realize(); +} + +template +relaxed_pos +concat_trees(Node* ltail, count_t ltcount, + Node* rroot, shift_t rshift, size_t rsize) +{ + return make_singleton_regular_sub_pos(ltail, ltcount).visit( + concat_trees_left_visitor{}, + empty_leaf_pos{}, + rroot, rshift, rsize) + .realize(); +} + +template +using concat_center_mut_pos = concat_center_pos; + +template +struct concat_merger_mut +{ + using node_t = Node; + using edit_t = typename Node::edit_t; + + static constexpr auto B = Node::bits; + static constexpr auto BL = Node::bits_leaf; + + using result_t = concat_center_pos; + + edit_t ec_ = {}; + + count_t* curr_; + count_t n_; + result_t result_; + count_t count_ = 0; + node_t* candidate_ = nullptr; + edit_t candidate_e_ = Node::memory::transience_t::noone; + + concat_merger_mut(edit_t ec, shift_t shift, + count_t* counts, count_t n, + edit_t candidate_e, node_t* candidate) + : ec_{ec} + , curr_{counts} + , n_{n} + , result_{shift + B, nullptr, 0} + { + if (candidate) { + candidate->ensure_mutable_relaxed_e(candidate_e, ec); + result_.nodes_[0] = candidate; + } else { + result_.nodes_[0] = node_t::make_inner_r_e(ec); + } + } + + node_t* to_ = {}; + count_t to_offset_ = {}; + size_t to_size_ = {}; + + void set_candidate(edit_t candidate_e, node_t* candidate) + { candidate_ = candidate; candidate_e_ = candidate_e; } + + void add_child(node_t* p, size_t size) + { + ++curr_; + auto parent = result_.nodes_[result_.count_ - 1]; + auto relaxed = parent->relaxed(); + if (count_ == branches) { + parent->relaxed()->d.count = count_; + assert(result_.count_ < result_t::max_children); + n_ -= branches; + if (candidate_) { + parent = candidate_; + parent->ensure_mutable_relaxed_e(candidate_e_, ec_); + candidate_ = nullptr; + } else + parent = node_t::make_inner_r_e(ec_); + count_ = 0; + relaxed = parent->relaxed(); + result_.nodes_[result_.count_] = parent; + result_.sizes_[result_.count_] = result_.sizes_[result_.count_ - 1]; + ++result_.count_; + } + auto idx = count_++; + result_.sizes_[result_.count_ - 1] += size; + relaxed->d.sizes[idx] = size + (idx ? relaxed->d.sizes[idx - 1] : 0); + parent->inner() [idx] = p; + }; + + template + void merge_leaf(Pos&& p, edit_t e) + { + auto from = p.node(); + auto from_size = p.size(); + auto from_count = p.count(); + assert(from_size); + if (!to_ && *curr_ == from_count) { + add_child(from, from_size); + } else { + auto from_offset = count_t{}; + auto from_data = from->leaf(); + auto from_mutate = from->can_mutate(e); + do { + if (!to_) { + if (from_mutate) { + node_t::ownee(from) = ec_; + to_ = from->inc(); + assert(from_count); + } else { + to_ = node_t::make_leaf_e(ec_); + } + to_offset_ = 0; + } + auto data = to_->leaf(); + auto to_copy = std::min(from_count - from_offset, + *curr_ - to_offset_); + if (from == to_) { + if (from_offset != to_offset_) + std::move(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + } else { + if (!from_mutate) + std::uninitialized_copy(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + else + uninitialized_move(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + } + to_offset_ += to_copy; + from_offset += to_copy; + if (*curr_ == to_offset_) { + add_child(to_, to_offset_); + to_ = nullptr; + } + } while (from_offset != from_count); + } + } + + template + void merge_inner(Pos&& p, edit_t e) + { + auto from = p.node(); + auto from_size = p.size(); + auto from_count = p.count(); + assert(from_size); + if (!to_ && *curr_ == from_count) { + add_child(from, from_size); + } else { + auto from_offset = count_t{}; + auto from_data = from->inner(); + auto from_mutate = from->can_relax() && from->can_mutate(e); + do { + if (!to_) { + if (from_mutate) { + node_t::ownee(from) = ec_; + from->ensure_mutable_relaxed_e(e, ec_); + to_ = from; + } else { + to_ = node_t::make_inner_r_e(ec_); + } + to_offset_ = 0; + to_size_ = 0; + } + auto data = to_->inner(); + auto to_copy = std::min(from_count - from_offset, + *curr_ - to_offset_); + auto sizes = to_->relaxed()->d.sizes; + if (from != to_ || from_offset != to_offset_) { + std::copy(from_data + from_offset, + from_data + from_offset + to_copy, + data + to_offset_); + p.copy_sizes(from_offset, to_copy, + to_size_, sizes + to_offset_); + } + to_offset_ += to_copy; + from_offset += to_copy; + to_size_ = sizes[to_offset_ - 1]; + if (*curr_ == to_offset_) { + to_->relaxed()->d.count = to_offset_; + add_child(to_, to_size_); + to_ = nullptr; + } + } while (from_offset != from_count); + } + } + + concat_center_pos finish() const + { + assert(!to_); + result_.nodes_[result_.count_ - 1]->relaxed()->d.count = count_; + return result_; + } + + void abort() + { + // We may have mutated stuff the tree in place, leaving + // everything in a corrupted state... It should be possible + // to define cleanup properly, but that is a task for some + // other day... ;) + std::terminate(); + } +}; + +struct concat_merger_mut_visitor +{ + using this_t = concat_merger_mut_visitor; + + template + friend void visit_inner(this_t, Pos&& p, + Merger& merger, edit_type e) + { merger.merge_inner(p, e); } + + template + friend void visit_leaf(this_t, Pos&& p, + Merger& merger, edit_type e) + { merger.merge_leaf(p, e); } +}; + +template +struct concat_rebalance_plan_mut : concat_rebalance_plan +{ + using this_t = concat_rebalance_plan_mut; + + template + concat_center_mut_pos> + merge(edit_type ec, + edit_type el, LPos&& lpos, CPos&& cpos, + edit_type er, RPos&& rpos) + { + using node_t = node_type; + using merger_t = concat_merger_mut; + using visitor_t = concat_merger_mut_visitor; + auto lnode = ((node_t*)lpos.node()); + auto rnode = ((node_t*)rpos.node()); + auto lmut2 = lnode && lnode->can_relax() && lnode->can_mutate(el); + auto rmut2 = rnode && rnode->can_relax() && rnode->can_mutate(er); + auto merger = merger_t{ + ec, cpos.shift(), this->counts, this->n, + el, lmut2 ? lnode : nullptr + }; + try { + lpos.each_left_sub(visitor_t{}, merger, el); + cpos.each_sub(visitor_t{}, merger, ec); + if (rmut2) merger.set_candidate(er, rnode); + rpos.each_right_sub(visitor_t{}, merger, er); + return merger.finish(); + } catch (...) { + merger.abort(); + throw; + } + } +}; + +template +concat_center_pos +concat_rebalance_mut(edit_type ec, + edit_type el, LPos&& lpos, CPos&& cpos, + edit_type er, RPos&& rpos) +{ + auto plan = concat_rebalance_plan_mut{}; + plan.fill(lpos, cpos, rpos); + plan.shuffle(cpos.shift()); + return plan.merge(ec, el, lpos, cpos, er, rpos); +} + +template +concat_center_mut_pos +concat_leafs_mut(edit_type ec, + edit_type el, LPos&& lpos, TPos&& tpos, + edit_type er, RPos&& rpos) +{ + static_assert(Node::bits >= 2, ""); + assert(lpos.shift() == tpos.shift()); + assert(lpos.shift() == rpos.shift()); + assert(lpos.shift() == 0); + if (tpos.count() > 0) + return { + Node::bits_leaf, + lpos.node(), lpos.count(), + tpos.node(), tpos.count(), + rpos.node(), rpos.count(), + }; + else + return { + Node::bits_leaf, + lpos.node(), lpos.count(), + rpos.node(), rpos.count(), + }; +} + +template +struct concat_left_mut_visitor; +template +struct concat_right_mut_visitor; +template +struct concat_both_mut_visitor; + +template +concat_center_mut_pos +concat_inners_mut(edit_type ec, + edit_type el, LPos&& lpos, TPos&& tpos, + edit_type er, RPos&& rpos) +{ + auto lshift = lpos.shift(); + auto rshift = rpos.shift(); + // lpos.node() can be null it is a singleton_regular_sub_pos<...>, + // this is, when the tree is just a tail... + if (lshift > rshift) { + auto cpos = lpos.last_sub(concat_left_mut_visitor{}, + ec, el, tpos, er, rpos); + return concat_rebalance_mut(ec, + el, lpos, cpos, + er, null_sub_pos{}); + } else if (lshift < rshift) { + auto cpos = rpos.first_sub(concat_right_mut_visitor{}, + ec, el, lpos, tpos, er); + return concat_rebalance_mut(ec, + el, null_sub_pos{}, cpos, + er, rpos); + } else { + assert(lshift == rshift); + assert(Node::bits_leaf == 0u || lshift > 0); + auto cpos = lpos.last_sub(concat_both_mut_visitor{}, + ec, el, tpos, er, rpos); + return concat_rebalance_mut(ec, + el, lpos, cpos, + er, rpos); + } +} + +template +struct concat_left_mut_visitor +{ + using this_t = concat_left_mut_visitor; + using edit_t = typename Node::edit_t; + + template + friend concat_center_mut_pos + visit_inner(this_t, LPos&& lpos, edit_t ec, + edit_t el, TPos&& tpos, + edit_t er, RPos&& rpos) + { return concat_inners_mut( + ec, el, lpos, tpos, er, rpos); } + + template + friend concat_center_mut_pos + visit_leaf(this_t, LPos&& lpos, edit_t ec, + edit_t el, TPos&& tpos, + edit_t er, RPos&& rpos) + { IMMER_UNREACHABLE; } +}; + +template +struct concat_right_mut_visitor +{ + using this_t = concat_right_mut_visitor; + using edit_t = typename Node::edit_t; + + template + friend concat_center_mut_pos + visit_inner(this_t, RPos&& rpos, edit_t ec, + edit_t el, LPos&& lpos, TPos&& tpos, + edit_t er) + { return concat_inners_mut( + ec, el, lpos, tpos, er, rpos); } + + template + friend concat_center_mut_pos + visit_leaf(this_t, RPos&& rpos, edit_t ec, + edit_t el, LPos&& lpos, TPos&& tpos, + edit_t er) + { return concat_leafs_mut( + ec, el, lpos, tpos, er, rpos); } +}; + +template +struct concat_both_mut_visitor +{ + using this_t = concat_both_mut_visitor; + using edit_t = typename Node::edit_t; + + template + friend concat_center_mut_pos + visit_inner(this_t, LPos&& lpos, edit_t ec, + edit_t el, TPos&& tpos, + edit_t er, RPos&& rpos) + { return rpos.first_sub(concat_right_mut_visitor{}, + ec, el, lpos, tpos, er); } + + template + friend concat_center_mut_pos + visit_leaf(this_t, LPos&& lpos, edit_t ec, + edit_t el, TPos&& tpos, + edit_t er, RPos&& rpos) + { return rpos.first_sub_leaf(concat_right_mut_visitor{}, + ec, el, lpos, tpos, er); } +}; + +template +struct concat_trees_right_mut_visitor +{ + using this_t = concat_trees_right_mut_visitor; + using edit_t = typename Node::edit_t; + + template + friend concat_center_mut_pos + visit_node(this_t, RPos&& rpos, edit_t ec, + edit_t el, LPos&& lpos, TPos&& tpos, + edit_t er) + { return concat_inners_mut( + ec, el, lpos, tpos, er, rpos); } +}; + +template +struct concat_trees_left_mut_visitor +{ + using this_t = concat_trees_left_mut_visitor; + using edit_t = typename Node::edit_t; + + template + friend concat_center_mut_pos + visit_node(this_t, LPos&& lpos, edit_t ec, + edit_t el, TPos&& tpos, + edit_t er, Args&& ...args) + { return visit_maybe_relaxed_sub( + args..., + concat_trees_right_mut_visitor{}, + ec, el, lpos, tpos, er); } +}; + +template +relaxed_pos +concat_trees_mut(edit_type ec, + edit_type el, + Node* lroot, shift_t lshift, size_t lsize, + Node* ltail, count_t ltcount, + edit_type er, + Node* rroot, shift_t rshift, size_t rsize) +{ + return visit_maybe_relaxed_sub( + lroot, lshift, lsize, + concat_trees_left_mut_visitor{}, + ec, + el, make_leaf_pos(ltail, ltcount), + er, rroot, rshift, rsize) + .realize_e(ec); +} + +template +relaxed_pos +concat_trees_mut(edit_type ec, + edit_type el, + Node* ltail, count_t ltcount, + edit_type er, + Node* rroot, shift_t rshift, size_t rsize) +{ + return make_singleton_regular_sub_pos(ltail, ltcount).visit( + concat_trees_left_mut_visitor{}, + ec, + el, empty_leaf_pos{}, + er, rroot, rshift, rsize) + .realize_e(ec); +} + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/position.hpp b/src/immer/detail/rbts/position.hpp new file mode 100644 index 000000000000..f4cdf88db68d --- /dev/null +++ b/src/immer/detail/rbts/position.hpp @@ -0,0 +1,1838 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +#include +#include +#include + +namespace immer { +namespace detail { +namespace rbts { + +template +constexpr auto bits = std::decay_t::node_t::bits; + +template +constexpr auto bits_leaf = std::decay_t::node_t::bits_leaf; + +template +using node_type = typename std::decay::type::node_t; + +template +using edit_type = typename std::decay::type::node_t::edit_t; + +template +struct empty_regular_pos +{ + using node_t = NodeT; + node_t* node_; + + count_t count() const { return 0; } + node_t* node() const { return node_; } + shift_t shift() const { return 0; } + size_t size() const { return 0; } + + template + void each(Visitor, Args&&...) {} + template + bool each_pred(Visitor, Args&&...) { return true; } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_regular(v, *this, std::forward(args)...); + } +}; + +template +empty_regular_pos make_empty_regular_pos(NodeT* node) +{ + return {node}; +} + +template +struct empty_leaf_pos +{ + using node_t = NodeT; + node_t* node_; + + count_t count() const { return 0; } + node_t* node() const { return node_; } + shift_t shift() const { return 0; } + size_t size() const { return 0; } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_leaf(v, *this, std::forward(args)...); + } +}; + +template +empty_leaf_pos make_empty_leaf_pos(NodeT* node) +{ + assert(node); + return {node}; +} + +template +struct leaf_pos +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + node_t* node_; + size_t size_; + + count_t count() const { return index(size_ - 1) + 1; } + node_t* node() const { return node_; } + size_t size() const { return size_; } + shift_t shift() const { return 0; } + count_t index(size_t idx) const { return idx & mask; } + count_t subindex(size_t idx) const { return idx; } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_leaf(v, *this, std::forward(args)...); + } +}; + +template +leaf_pos make_leaf_pos(NodeT* node, size_t size) +{ + assert(node); + assert(size > 0); + return {node, size}; +} + +template +struct leaf_sub_pos +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + node_t* node_; + count_t count_; + + count_t count() const { return count_; } + node_t* node() const { return node_; } + size_t size() const { return count_; } + shift_t shift() const { return 0; } + count_t index(size_t idx) const { return idx & mask; } + count_t subindex(size_t idx) const { return idx; } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_leaf(v, *this, std::forward(args)...); + } +}; + +template +leaf_sub_pos make_leaf_sub_pos(NodeT* node, count_t count) +{ + assert(node); + assert(count <= branches); + return {node, count}; +} + +template +struct leaf_descent_pos +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + node_t* node_; + + node_t* node() const { return node_; } + shift_t shift() const { return 0; } + count_t index(size_t idx) const { return idx & mask; } + + template + decltype(auto) descend(Args&&...) {} + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_leaf(v, *this, std::forward(args)...); + } +}; + +template +leaf_descent_pos make_leaf_descent_pos(NodeT* node) +{ + assert(node); + return {node}; +} + +template +struct full_leaf_pos +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + node_t* node_; + + count_t count() const { return branches; } + node_t* node() const { return node_; } + size_t size() const { return branches; } + shift_t shift() const { return 0; } + count_t index(size_t idx) const { return idx & mask; } + count_t subindex(size_t idx) const { return idx; } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_leaf(v, *this, std::forward(args)...); + } +}; + +template +full_leaf_pos make_full_leaf_pos(NodeT* node) +{ + assert(node); + return {node}; +} + +template +struct regular_pos +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + node_t* node_; + shift_t shift_; + size_t size_; + + count_t count() const { return index(size_ - 1) + 1; } + node_t* node() const { return node_; } + size_t size() const { return size_; } + shift_t shift() const { return shift_; } + count_t index(size_t idx) const { return (idx >> shift_) & mask; } + count_t subindex(size_t idx) const { return idx >> shift_; } + size_t this_size() const { return ((size_ - 1) & ~(~size_t{} << (shift_ + B))) + 1; } + + template + void each(Visitor v, Args&&... args) + { return each_regular(*this, v, args...); } + + template + bool each_pred(Visitor v, Args&&... args) + { return each_pred_regular(*this, v, args...); } + + template + bool each_pred_zip(Visitor v, node_t* other, Args&&... args) + { return each_pred_zip_regular(*this, v, other, args...); } + + template + bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args) + { return each_pred_i_regular(*this, v, i, n, args...); } + + template + bool each_pred_right(Visitor v, count_t start, Args&&... args) + { return each_pred_right_regular(*this, v, start, args...); } + + template + bool each_pred_left(Visitor v, count_t n, Args&&... args) + { return each_pred_left_regular(*this, v, n, args...); } + + template + void each_i(Visitor v, count_t i, count_t n, Args&&... args) + { return each_i_regular(*this, v, i, n, args...); } + + template + void each_right(Visitor v, count_t start, Args&&... args) + { return each_right_regular(*this, v, start, args...); } + + template + void each_left(Visitor v, count_t n, Args&&... args) + { return each_left_regular(*this, v, n, args...); } + + template + decltype(auto) towards(Visitor v, size_t idx, Args&&... args) + { return towards_oh_ch_regular(*this, v, idx, index(idx), count(), args...); } + + template + decltype(auto) towards_oh(Visitor v, size_t idx, + count_t offset_hint, + Args&&... args) + { return towards_oh_ch_regular(*this, v, idx, offset_hint, count(), args...); } + + template + decltype(auto) towards_oh_ch(Visitor v, size_t idx, + count_t offset_hint, + count_t count_hint, + Args&&... args) + { return towards_oh_ch_regular(*this, v, idx, offset_hint, count(), args...); } + + template + decltype(auto) towards_sub_oh(Visitor v, size_t idx, + count_t offset_hint, + Args&&... args) + { return towards_sub_oh_regular(*this, v, idx, offset_hint, args...); } + + template + decltype(auto) last_oh(Visitor v, count_t offset_hint, Args&&... args) + { return last_oh_regular(*this, v, offset_hint, args...); } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_regular(v, *this, std::forward(args)...); + } +}; + +template +void each_regular(Pos&& p, Visitor v, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + auto n = p.node()->inner(); + auto last = p.count() - 1; + auto e = n + last; + if (p.shift() == BL) { + for (; n != e; ++n) { + IMMER_PREFETCH(n + 1); + make_full_leaf_pos(*n).visit(v, args...); + } + make_leaf_pos(*n, p.size()).visit(v, args...); + } else { + auto ss = p.shift() - B; + for (; n != e; ++n) + make_full_pos(*n, ss).visit(v, args...); + make_regular_pos(*n, ss, p.size()).visit(v, args...); + } +} + +template +bool each_pred_regular(Pos&& p, Visitor v, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + auto n = p.node()->inner(); + auto last = p.count() - 1; + auto e = n + last; + if (p.shift() == BL) { + for (; n != e; ++n) { + IMMER_PREFETCH(n + 1); + if (!make_full_leaf_pos(*n).visit(v, args...)) + return false; + } + return make_leaf_pos(*n, p.size()).visit(v, args...); + } else { + auto ss = p.shift() - B; + for (; n != e; ++n) + if (!make_full_pos(*n, ss).visit(v, args...)) + return false; + return make_regular_pos(*n, ss, p.size()).visit(v, args...); + } +} + +template +bool each_pred_zip_regular(Pos&& p, Visitor v, node_type* other, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + + auto n = p.node()->inner(); + auto n2 = other->inner(); + auto last = p.count() - 1; + auto e = n + last; + if (p.shift() == BL) { + for (; n != e; ++n, ++n2) { + IMMER_PREFETCH(n + 1); + IMMER_PREFETCH(n2 + 1); + if (!make_full_leaf_pos(*n).visit(v, *n2, args...)) + return false; + } + return make_leaf_pos(*n, p.size()).visit(v, *n2, args...); + } else { + auto ss = p.shift() - B; + for (; n != e; ++n, ++n2) + if (!make_full_pos(*n, ss).visit(v, *n2, args...)) + return false; + return make_regular_pos(*n, ss, p.size()).visit(v, *n2, args...); + } +} + +template +bool each_pred_i_regular(Pos&& p, Visitor v, count_t f, count_t l, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + + if (p.shift() == BL) { + if (l > f) { + if (l < p.count()) { + auto n = p.node()->inner() + f; + auto e = p.node()->inner() + l; + for (; n < e; ++n) { + IMMER_PREFETCH(n + 1); + if (!make_full_leaf_pos(*n).visit(v, args...)) + return false; + } + } else { + auto n = p.node()->inner() + f; + auto e = p.node()->inner() + l - 1; + for (; n < e; ++n) { + IMMER_PREFETCH(n + 1); + if (!make_full_leaf_pos(*n).visit(v, args...)) + return false; + } + if (!make_leaf_pos(*n, p.size()).visit(v, args...)) + return false; + } + } + } else { + if (l > f) { + auto ss = p.shift() - B; + if (l < p.count()) { + auto n = p.node()->inner() + f; + auto e = p.node()->inner() + l; + for (; n < e; ++n) + if (!make_full_pos(*n, ss).visit(v, args...)) + return false; + } else { + auto n = p.node()->inner() + f; + auto e = p.node()->inner() + l - 1; + for (; n < e; ++n) + if (!make_full_pos(*n, ss).visit(v, args...)) + return false; + if (!make_regular_pos(*n, ss, p.size()).visit(v, args...)) + return false; + } + } + } + return true; +} + +template +bool each_pred_left_regular(Pos&& p, Visitor v, count_t last, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + assert(last < p.count()); + if (p.shift() == BL) { + auto n = p.node()->inner(); + auto e = n + last; + for (; n != e; ++n) { + IMMER_PREFETCH(n + 1); + if (!make_full_leaf_pos(*n).visit(v, args...)) + return false; + } + } else { + auto n = p.node()->inner(); + auto e = n + last; + auto ss = p.shift() - B; + for (; n != e; ++n) + if (!make_full_pos(*n, ss).visit(v, args...)) + return false; + } + return true; +} + +template +bool each_pred_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + + if (p.shift() == BL) { + auto n = p.node()->inner() + start; + auto last = p.count() - 1; + auto e = p.node()->inner() + last; + if (n <= e) { + for (; n != e; ++n) { + IMMER_PREFETCH(n + 1); + if (!make_full_leaf_pos(*n).visit(v, args...)) + return false; + } + if (!make_leaf_pos(*n, p.size()).visit(v, args...)) + return false; + } + } else { + auto n = p.node()->inner() + start; + auto last = p.count() - 1; + auto e = p.node()->inner() + last; + auto ss = p.shift() - B; + if (n <= e) { + for (; n != e; ++n) + if (!make_full_pos(*n, ss).visit(v, args...)) + return false; + if (!make_regular_pos(*n, ss, p.size()).visit(v, args...)) + return false; + } + } + return true; +} + +template +void each_i_regular(Pos&& p, Visitor v, count_t f, count_t l, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + + if (p.shift() == BL) { + if (l > f) { + if (l < p.count()) { + auto n = p.node()->inner() + f; + auto e = p.node()->inner() + l; + for (; n < e; ++n) { + IMMER_PREFETCH(n + 1); + make_full_leaf_pos(*n).visit(v, args...); + } + } else { + auto n = p.node()->inner() + f; + auto e = p.node()->inner() + l - 1; + for (; n < e; ++n) { + IMMER_PREFETCH(n + 1); + make_full_leaf_pos(*n).visit(v, args...); + } + make_leaf_pos(*n, p.size()).visit(v, args...); + } + } + } else { + if (l > f) { + auto ss = p.shift() - B; + if (l < p.count()) { + auto n = p.node()->inner() + f; + auto e = p.node()->inner() + l; + for (; n < e; ++n) + make_full_pos(*n, ss).visit(v, args...); + } else { + auto n = p.node()->inner() + f; + auto e = p.node()->inner() + l - 1; + for (; n < e; ++n) + make_full_pos(*n, ss).visit(v, args...); + make_regular_pos(*n, ss, p.size()).visit(v, args...); + } + } + } +} + +template +void each_left_regular(Pos&& p, Visitor v, count_t last, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + assert(last < p.count()); + if (p.shift() == BL) { + auto n = p.node()->inner(); + auto e = n + last; + for (; n != e; ++n) { + IMMER_PREFETCH(n + 1); + make_full_leaf_pos(*n).visit(v, args...); + } + } else { + auto n = p.node()->inner(); + auto e = n + last; + auto ss = p.shift() - B; + for (; n != e; ++n) + make_full_pos(*n, ss).visit(v, args...); + } +} + +template +void each_right_regular(Pos&& p, Visitor v, count_t start, Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + + if (p.shift() == BL) { + auto n = p.node()->inner() + start; + auto last = p.count() - 1; + auto e = p.node()->inner() + last; + if (n <= e) { + for (; n != e; ++n) { + IMMER_PREFETCH(n + 1); + make_full_leaf_pos(*n).visit(v, args...); + } + make_leaf_pos(*n, p.size()).visit(v, args...); + } + } else { + auto n = p.node()->inner() + start; + auto last = p.count() - 1; + auto e = p.node()->inner() + last; + auto ss = p.shift() - B; + if (n <= e) { + for (; n != e; ++n) + make_full_pos(*n, ss).visit(v, args...); + make_regular_pos(*n, ss, p.size()).visit(v, args...); + } + } +} + +template +decltype(auto) towards_oh_ch_regular(Pos&& p, Visitor v, size_t idx, + count_t offset_hint, + count_t count_hint, + Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + assert(offset_hint == p.index(idx)); + assert(count_hint == p.count()); + auto is_leaf = p.shift() == BL; + auto child = p.node()->inner() [offset_hint]; + auto is_full = offset_hint + 1 != count_hint; + return is_full + ? (is_leaf + ? make_full_leaf_pos(child).visit(v, idx, args...) + : make_full_pos(child, p.shift() - B).visit(v, idx, args...)) + : (is_leaf + ? make_leaf_pos(child, p.size()).visit(v, idx, args...) + : make_regular_pos(child, p.shift() - B, p.size()).visit(v, idx, args...)); +} + +template +decltype(auto) towards_sub_oh_regular(Pos&& p, Visitor v, size_t idx, + count_t offset_hint, + Args&&... args) +{ + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + assert(offset_hint == p.index(idx)); + auto is_leaf = p.shift() == BL; + auto child = p.node()->inner() [offset_hint]; + auto lsize = offset_hint << p.shift(); + auto size = p.this_size(); + auto is_full = (size - lsize) >= (size_t{1} << p.shift()); + return is_full + ? (is_leaf + ? make_full_leaf_pos(child).visit( + v, idx - lsize, args...) + : make_full_pos(child, p.shift() - B).visit( + v, idx - lsize, args...)) + : (is_leaf + ? make_leaf_sub_pos(child, size - lsize).visit( + v, idx - lsize, args...) + : make_regular_sub_pos(child, p.shift() - B, size - lsize).visit( + v, idx - lsize, args...)); +} + +template +decltype(auto) last_oh_regular(Pos&& p, Visitor v, + count_t offset_hint, + Args&&... args) +{ + assert(offset_hint == p.count() - 1); + constexpr auto B = bits; + constexpr auto BL = bits_leaf; + auto child = p.node()->inner() [offset_hint]; + auto is_leaf = p.shift() == BL; + return is_leaf + ? make_leaf_pos(child, p.size()).visit(v, args...) + : make_regular_pos(child, p.shift() - B, p.size()).visit(v, args...); +} + +template +regular_pos make_regular_pos(NodeT* node, + shift_t shift, + size_t size) +{ + assert(node); + assert(shift >= NodeT::bits_leaf); + assert(size > 0); + return {node, shift, size}; +} + +struct null_sub_pos +{ + auto node() const { return nullptr; } + + template + void each_sub(Visitor, Args&&...) {} + template + void each_right_sub(Visitor, Args&&...) {} + template + void each_left_sub(Visitor, Args&&...) {} + template + void visit(Visitor, Args&&...) {} +}; + +template +struct singleton_regular_sub_pos +{ + // this is a fake regular pos made out of a single child... useful + // to treat a single leaf node as a whole tree + + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + node_t* leaf_; + count_t count_; + + count_t count() const { return 1; } + node_t* node() const { return nullptr; } + size_t size() const { return count_; } + shift_t shift() const { return BL; } + count_t index(size_t idx) const { return 0; } + count_t subindex(size_t idx) const { return 0; } + size_t size_before(count_t offset) const { return 0; } + size_t this_size() const { return count_; } + size_t size(count_t offset) { return count_; } + + template + void each_left_sub(Visitor v, Args&&... args) {} + template + void each(Visitor v, Args&&... args) {} + + template + decltype(auto) last_sub(Visitor v, Args&&... args) + { + return make_leaf_sub_pos(leaf_, count_).visit(v, args...); + } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_regular(v, *this, std::forward(args)...); + } +}; + +template +auto make_singleton_regular_sub_pos(NodeT* leaf, count_t count) +{ + assert(leaf); + assert(leaf->kind() == NodeT::kind_t::leaf); + assert(count > 0); + return singleton_regular_sub_pos{leaf, count}; +} + +template +struct regular_sub_pos +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + node_t* node_; + shift_t shift_; + size_t size_; + + count_t count() const { return subindex(size_ - 1) + 1; } + node_t* node() const { return node_; } + size_t size() const { return size_; } + shift_t shift() const { return shift_; } + count_t index(size_t idx) const { return (idx >> shift_) & mask; } + count_t subindex(size_t idx) const { return idx >> shift_; } + size_t size_before(count_t offset) const { return offset << shift_; } + size_t this_size() const { return size_; } + + auto size(count_t offset) + { + return offset == subindex(size_ - 1) + ? size_ - size_before(offset) + : 1 << shift_; + } + + auto size_sbh(count_t offset, size_t size_before_hint) + { + assert(size_before_hint == size_before(offset)); + return offset == subindex(size_ - 1) + ? size_ - size_before_hint + : 1 << shift_; + } + + void copy_sizes(count_t offset, + count_t n, + size_t init, + size_t* sizes) + { + if (n) { + auto last = offset + n - 1; + auto e = sizes + n - 1; + for (; sizes != e; ++sizes) + init = *sizes = init + (1 << shift_); + *sizes = init + size(last); + } + } + + template + void each(Visitor v, Args&& ...args) + { return each_regular(*this, v, args...); } + + template + bool each_pred(Visitor v, Args&& ...args) + { return each_pred_regular(*this, v, args...); } + + template + bool each_pred_zip(Visitor v, node_t* other, Args&&... args) + { return each_pred_zip_regular(*this, v, other, args...); } + + template + bool each_pred_i(Visitor v, count_t i, count_t n, Args&& ...args) + { return each_pred_i_regular(*this, v, i, n, args...); } + + template + bool each_pred_right(Visitor v, count_t start, Args&& ...args) + { return each_pred_right_regular(*this, v, start, args...); } + + template + bool each_pred_left(Visitor v, count_t last, Args&& ...args) + { return each_pred_left_regular(*this, v, last, args...); } + + template + void each_i(Visitor v, count_t i, count_t n, Args&& ...args) + { return each_i_regular(*this, v, i, n, args...); } + + template + void each_right(Visitor v, count_t start, Args&& ...args) + { return each_right_regular(*this, v, start, args...); } + + template + void each_left(Visitor v, count_t last, Args&& ...args) + { return each_left_regular(*this, v, last, args...); } + + template + void each_right_sub_(Visitor v, count_t i, Args&& ...args) + { + auto last = count() - 1; + auto lsize = size_ - (last << shift_); + auto n = node()->inner() + i; + auto e = node()->inner() + last; + if (shift() == BL) { + for (; n != e; ++n) { + IMMER_PREFETCH(n + 1); + make_full_leaf_pos(*n).visit(v, args...); + } + make_leaf_sub_pos(*n, lsize).visit(v, args...); + } else { + auto ss = shift_ - B; + for (; n != e; ++n) + make_full_pos(*n, ss).visit(v, args...); + make_regular_sub_pos(*n, ss, lsize).visit(v, args...); + } + } + + template + void each_sub(Visitor v, Args&& ...args) + { each_right_sub_(v, 0, args...); } + + template + void each_right_sub(Visitor v, Args&& ...args) + { if (count() > 1) each_right_sub_(v, 1, args...); } + + template + void each_left_sub(Visitor v, Args&& ...args) + { each_left(v, count() - 1, args...); } + + template + decltype(auto) towards(Visitor v, size_t idx, Args&&... args) + { return towards_oh_ch_regular(*this, v, idx, index(idx), count(), args...); } + + template + decltype(auto) towards_oh(Visitor v, size_t idx, + count_t offset_hint, + Args&&... args) + { return towards_oh_ch_regular(*this, v, idx, offset_hint, count(), args...); } + + template + decltype(auto) towards_oh_ch(Visitor v, size_t idx, + count_t offset_hint, + count_t count_hint, + Args&&... args) + { return towards_oh_ch_regular(*this, v, idx, offset_hint, count(), args...); } + + template + decltype(auto) towards_sub_oh(Visitor v, size_t idx, + count_t offset_hint, + Args&& ...args) + { return towards_sub_oh_regular(*this, v, idx, offset_hint, args...); } + + template + decltype(auto) last_oh(Visitor v, count_t offset_hint, Args&&... args) + { return last_oh_regular(*this, v, offset_hint, args...); } + + template + decltype(auto) last_sub(Visitor v, Args&&... args) + { + auto offset = count() - 1; + auto child = node_->inner() [offset]; + auto is_leaf = shift_ == BL; + auto lsize = size_ - (offset << shift_); + return is_leaf + ? make_leaf_sub_pos(child, lsize).visit(v, args...) + : make_regular_sub_pos(child, shift_ - B, lsize).visit(v, args...); + } + + template + decltype(auto) first_sub(Visitor v, Args&&... args) + { + auto is_leaf = shift_ == BL; + auto child = node_->inner() [0]; + auto is_full = size_ >= (size_t{1} << shift_); + return is_full + ? (is_leaf + ? make_full_leaf_pos(child).visit(v, args...) + : make_full_pos(child, shift_ - B).visit(v, args...)) + : (is_leaf + ? make_leaf_sub_pos(child, size_).visit(v, args...) + : make_regular_sub_pos(child, shift_ - B, size_).visit(v, args...)); + } + + template + decltype(auto) first_sub_leaf(Visitor v, Args&&... args) + { + assert(shift_ == BL); + auto child = node_->inner() [0]; + auto is_full = size_ >= branches; + return is_full + ? make_full_leaf_pos(child).visit(v, args...) + : make_leaf_sub_pos(child, size_).visit(v, args...); + } + + template + decltype(auto) first_sub_inner(Visitor v, Args&&... args) + { + assert(shift_ >= BL); + auto child = node_->inner() [0]; + auto is_full = size_ >= branches; + return is_full + ? make_full_pos(child, shift_ - B).visit(v, args...) + : make_regular_sub_pos(child, shift_ - B, size_).visit(v, args...); + } + + template + decltype(auto) nth_sub(count_t idx, Visitor v, Args&&... args) + { + assert(idx < count()); + auto is_leaf = shift_ == BL; + auto child = node_->inner() [idx]; + auto lsize = size(idx); + auto is_full = idx + 1 < count(); + return is_full + ? (is_leaf + ? make_full_leaf_pos(child).visit(v, args...) + : make_full_pos(child, shift_ - B).visit(v, args...)) + : (is_leaf + ? make_leaf_sub_pos(child, lsize).visit(v, args...) + : make_regular_sub_pos(child, shift_ - B, lsize).visit(v, args...)); + } + + template + decltype(auto) nth_sub_leaf(count_t idx, Visitor v, Args&&... args) + { + assert(shift_ == BL); + auto child = node_->inner() [idx]; + auto lsize = size(idx); + auto is_full = idx + 1 < count(); + return is_full + ? make_full_leaf_pos(child).visit(v, args...) + : make_leaf_sub_pos(child, lsize).visit(v, args...); + } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_regular(v, *this, std::forward(args)...); + } +}; + +template +regular_sub_pos make_regular_sub_pos(NodeT* node, + shift_t shift, + size_t size) +{ + assert(node); + assert(shift >= NodeT::bits_leaf); + assert(size > 0); + assert(size <= (branches << shift)); + return {node, shift, size}; +} + +template +struct regular_descent_pos +{ + static_assert(Shift > 0, "not leaf..."); + + using node_t = NodeT; + node_t* node_; + + node_t* node() const { return node_; } + shift_t shift() const { return Shift; } + count_t index(size_t idx) const { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshift-count-overflow" + return (idx >> Shift) & mask; +#pragma GCC diagnostic pop + } + + template + decltype(auto) descend(Visitor v, size_t idx) + { + auto offset = index(idx); + auto child = node_->inner()[offset]; + return regular_descent_pos{child}.visit(v, idx); + } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_regular(v, *this, std::forward(args)...); + } +}; + +template +struct regular_descent_pos +{ + using node_t = NodeT; + node_t* node_; + + node_t* node() const { return node_; } + shift_t shift() const { return BL; } + count_t index(size_t idx) const { return (idx >> BL) & mask; } + + template + decltype(auto) descend(Visitor v, size_t idx) + { + auto offset = index(idx); + auto child = node_->inner()[offset]; + return make_leaf_descent_pos(child).visit(v, idx); + } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_regular(v, *this, std::forward(args)...); + } +}; + +template +decltype(auto) visit_regular_descent(NodeT* node, shift_t shift, Visitor v, + size_t idx) +{ + constexpr auto B = NodeT::bits; + constexpr auto BL = NodeT::bits_leaf; + assert(node); + assert(shift >= BL); + switch (shift) { + case BL + B * 0: return regular_descent_pos{node}.visit(v, idx); + case BL + B * 1: return regular_descent_pos{node}.visit(v, idx); + case BL + B * 2: return regular_descent_pos{node}.visit(v, idx); + case BL + B * 3: return regular_descent_pos{node}.visit(v, idx); + case BL + B * 4: return regular_descent_pos{node}.visit(v, idx); + case BL + B * 5: return regular_descent_pos{node}.visit(v, idx); +#if IMMER_DESCENT_DEEP + default: + for (auto level = shift; level != endshift; level -= B) + node = node->inner() [(idx >> level) & mask]; + return make_leaf_descent_pos(node).visit(v, idx); +#endif // IMMER_DEEP_DESCENT + } + IMMER_UNREACHABLE; +} + +template +struct full_pos +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + node_t* node_; + shift_t shift_; + + count_t count() const { return branches; } + node_t* node() const { return node_; } + size_t size() const { return branches << shift_; } + shift_t shift() const { return shift_; } + count_t index(size_t idx) const { return (idx >> shift_) & mask; } + count_t subindex(size_t idx) const { return idx >> shift_; } + size_t size(count_t offset) const { return 1 << shift_; } + size_t size_sbh(count_t offset, size_t) const { return 1 << shift_; } + size_t size_before(count_t offset) const { return offset << shift_; } + + void copy_sizes(count_t offset, + count_t n, + size_t init, + size_t* sizes) + { + auto e = sizes + n; + for (; sizes != e; ++sizes) + init = *sizes = init + (1 << shift_); + } + + template + void each(Visitor v, Args&&... args) + { + auto p = node_->inner(); + auto e = p + branches; + if (shift_ == BL) { + for (; p != e; ++p) { + IMMER_PREFETCH(p + 1); + make_full_leaf_pos(*p).visit(v, args...); + } + } else { + auto ss = shift_ - B; + for (; p != e; ++p) + make_full_pos(*p, ss).visit(v, args...); + } + } + + template + bool each_pred(Visitor v, Args&&... args) + { + auto p = node_->inner(); + auto e = p + branches; + if (shift_ == BL) { + for (; p != e; ++p) { + IMMER_PREFETCH(p + 1); + if (!make_full_leaf_pos(*p).visit(v, args...)) + return false; + } + } else { + auto ss = shift_ - B; + for (; p != e; ++p) + if (!make_full_pos(*p, ss).visit(v, args...)) + return false; + } + return true; + } + + template + bool each_pred_zip(Visitor v, node_t* other, Args&&... args) + { + auto p = node_->inner(); + auto p2 = other->inner(); + auto e = p + branches; + if (shift_ == BL) { + for (; p != e; ++p, ++p2) { + IMMER_PREFETCH(p + 1); + if (!make_full_leaf_pos(*p).visit(v, *p2, args...)) + return false; + } + } else { + auto ss = shift_ - B; + for (; p != e; ++p, ++p2) + if (!make_full_pos(*p, ss).visit(v, *p2, args...)) + return false; + } + return true; + } + + template + bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args) + { + auto p = node_->inner() + i; + auto e = node_->inner() + n; + if (shift_ == BL) { + for (; p != e; ++p) { + IMMER_PREFETCH(p + 1); + if (!make_full_leaf_pos(*p).visit(v, args...)) + return false; + } + } else { + auto ss = shift_ - B; + for (; p != e; ++p) + if (!make_full_pos(*p, ss).visit(v, args...)) + return false; + } + return true; + } + + template + void each_i(Visitor v, count_t i, count_t n, Args&&... args) + { + auto p = node_->inner() + i; + auto e = node_->inner() + n; + if (shift_ == BL) { + for (; p != e; ++p) { + IMMER_PREFETCH(p + 1); + make_full_leaf_pos(*p).visit(v, args...); + } + } else { + auto ss = shift_ - B; + for (; p != e; ++p) + make_full_pos(*p, ss).visit(v, args...); + } + } + + template + bool each_pred_right(Visitor v, count_t start, Args&&... args) + { return each_pred_i(v, start, branches, args...); } + + template + bool each_pred_left(Visitor v, count_t last, Args&&... args) + { return each_pred_i(v, 0, last, args...); } + + template + void each_sub(Visitor v, Args&&... args) + { each(v, args...); } + + template + void each_left_sub(Visitor v, Args&&... args) + { each_i(v, 0, branches - 1, args...); } + + template + void each_right_sub(Visitor v, Args&&... args) + { each_i(v, 1, branches, args...); } + + template + void each_right(Visitor v, count_t start, Args&&... args) + { each_i(v, start, branches, args...); } + + template + void each_left(Visitor v, count_t last, Args&&... args) + { each_i(v, 0, last, args...); } + + template + decltype(auto) towards(Visitor v, size_t idx, Args&&... args) + { return towards_oh(v, idx, index(idx), args...); } + + template + decltype(auto) towards_oh_ch(Visitor v, size_t idx, + count_t offset_hint, count_t, + Args&&... args) + { return towards_oh(v, idx, offset_hint, args...); } + + template + decltype(auto) towards_oh(Visitor v, size_t idx, + count_t offset_hint, + Args&&... args) + { + assert(offset_hint == index(idx)); + auto is_leaf = shift_ == BL; + auto child = node_->inner() [offset_hint]; + return is_leaf + ? make_full_leaf_pos(child).visit(v, idx, args...) + : make_full_pos(child, shift_ - B).visit(v, idx, args...); + } + + template + decltype(auto) towards_sub_oh(Visitor v, size_t idx, + count_t offset_hint, + Args&&... args) + { + assert(offset_hint == index(idx)); + auto is_leaf = shift_ == BL; + auto child = node_->inner() [offset_hint]; + auto lsize = offset_hint << shift_; + return is_leaf + ? make_full_leaf_pos(child).visit(v, idx - lsize, args...) + : make_full_pos(child, shift_ - B).visit(v, idx - lsize, args...); + } + + template + decltype(auto) first_sub(Visitor v, Args&&... args) + { + auto is_leaf = shift_ == BL; + auto child = node_->inner() [0]; + return is_leaf + ? make_full_leaf_pos(child).visit(v, args...) + : make_full_pos(child, shift_ - B).visit(v, args...); + } + + template + decltype(auto) first_sub_leaf(Visitor v, Args&&... args) + { + assert(shift_ == BL); + auto child = node_->inner() [0]; + return make_full_leaf_pos(child).visit(v, args...); + } + + template + decltype(auto) first_sub_inner(Visitor v, Args&&... args) + { + assert(shift_ >= BL); + auto child = node_->inner() [0]; + return make_full_pos(child, shift_ - B).visit(v, args...); + } + + template + decltype(auto) nth_sub(count_t idx, Visitor v, Args&&... args) + { + assert(idx < count()); + auto is_leaf = shift_ == BL; + auto child = node_->inner() [idx]; + return is_leaf + ? make_full_leaf_pos(child).visit(v, args...) + : make_full_pos(child, shift_ - B).visit(v, args...); + } + + template + decltype(auto) nth_sub_leaf(count_t idx, Visitor v, Args&&... args) + { + assert(shift_ == BL); + assert(idx < count()); + auto child = node_->inner() [idx]; + return make_full_leaf_pos(child).visit(v, args...); + } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_regular(v, *this, std::forward(args)...); + } +}; + +template +full_pos make_full_pos(NodeT* node, shift_t shift) +{ + assert(node); + assert(shift >= NodeT::bits_leaf); + return {node, shift}; +} + +template +struct relaxed_pos +{ + static constexpr auto B = NodeT::bits; + static constexpr auto BL = NodeT::bits_leaf; + + using node_t = NodeT; + using relaxed_t = typename NodeT::relaxed_t; + node_t* node_; + shift_t shift_; + relaxed_t* relaxed_; + + count_t count() const { return relaxed_->d.count; } + node_t* node() const { return node_; } + size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; } + shift_t shift() const { return shift_; } + count_t subindex(size_t idx) const { return index(idx); } + relaxed_t* relaxed() const { return relaxed_; } + + size_t size_before(count_t offset) const + { return offset ? relaxed_->d.sizes[offset - 1] : 0; } + + size_t size(count_t offset) const + { return size_sbh(offset, size_before(offset)); } + + size_t size_sbh(count_t offset, size_t size_before_hint) const + { + assert(size_before_hint == size_before(offset)); + return relaxed_->d.sizes[offset] - size_before_hint; + } + + count_t index(size_t idx) const + { + auto offset = idx >> shift_; + while (relaxed_->d.sizes[offset] <= idx) ++offset; + return offset; + } + + void copy_sizes(count_t offset, + count_t n, + size_t init, + size_t* sizes) + { + auto e = sizes + n; + auto prev = size_before(offset); + auto these = relaxed_->d.sizes + offset; + for (; sizes != e; ++sizes, ++these) { + auto this_size = *these; + init = *sizes = init + (this_size - prev); + prev = this_size; + } + } + + template + void each(Visitor v, Args&&... args) + { each_left(v, relaxed_->d.count, args...); } + + template + bool each_pred(Visitor v, Args&&... args) + { + auto p = node_->inner(); + auto s = size_t{}; + auto n = count(); + if (shift_ == BL) { + for (auto i = count_t{0}; i < n; ++i) { + IMMER_PREFETCH(p + i + 1); + if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) + .visit(v, args...)) + return false; + s = relaxed_->d.sizes[i]; + } + } else { + auto ss = shift_ - B; + for (auto i = count_t{0}; i < n; ++i) { + if (!visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s, + v, args...)) + return false; + s = relaxed_->d.sizes[i]; + } + } + return true; + } + + template + bool each_pred_i(Visitor v, count_t i, count_t n, Args&&... args) + { + if (shift_ == BL) { + auto p = node_->inner(); + auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0; + for (; i < n; ++i) { + IMMER_PREFETCH(p + i + 1); + if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) + .visit(v, args...)) + return false; + s = relaxed_->d.sizes[i]; + } + } else { + auto p = node_->inner(); + auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0; + auto ss = shift_ - B; + for (; i < n; ++i) { + if (!visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s, + v, args...)) + return false; + s = relaxed_->d.sizes[i]; + } + } + return true; + } + + template + bool each_pred_left(Visitor v, count_t n, Args&&... args) + { + auto p = node_->inner(); + auto s = size_t{}; + if (shift_ == BL) { + for (auto i = count_t{0}; i < n; ++i) { + IMMER_PREFETCH(p + i + 1); + if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) + .visit(v, args...)) + return false; + s = relaxed_->d.sizes[i]; + } + } else { + auto ss = shift_ - B; + for (auto i = count_t{0}; i < n; ++i) { + if (!visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s, + v, args...)) + return false; + s = relaxed_->d.sizes[i]; + } + } + return true; + } + + template + bool each_pred_right(Visitor v, count_t start, Args&&... args) + { + assert(start > 0); + assert(start <= relaxed_->d.count); + auto s = relaxed_->d.sizes[start - 1]; + auto p = node_->inner(); + if (shift_ == BL) { + for (auto i = start; i < relaxed_->d.count; ++i) { + IMMER_PREFETCH(p + i + 1); + if (!make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) + .visit(v, args...)) + return false; + s = relaxed_->d.sizes[i]; + } + } else { + auto ss = shift_ - B; + for (auto i = start; i < relaxed_->d.count; ++i) { + if (!visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s, + v, args...)) + return false; + s = relaxed_->d.sizes[i]; + } + } + return true; + } + + template + void each_i(Visitor v, count_t i, count_t n, Args&&... args) + { + if (shift_ == BL) { + auto p = node_->inner(); + auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0; + for (; i < n; ++i) { + IMMER_PREFETCH(p + i + 1); + make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) + .visit(v, args...); + s = relaxed_->d.sizes[i]; + } + } else { + auto p = node_->inner(); + auto s = i > 0 ? relaxed_->d.sizes[i - 1] : 0; + auto ss = shift_ - B; + for (; i < n; ++i) { + visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s, + v, args...); + s = relaxed_->d.sizes[i]; + } + } + } + + template + void each_sub(Visitor v, Args&&... args) + { each_left(v, relaxed_->d.count, args...); } + + template + void each_left_sub(Visitor v, Args&&... args) + { each_left(v, relaxed_->d.count - 1, args...); } + + template + void each_left(Visitor v, count_t n, Args&&... args) + { + auto p = node_->inner(); + auto s = size_t{}; + if (shift_ == BL) { + for (auto i = count_t{0}; i < n; ++i) { + IMMER_PREFETCH(p + i + 1); + make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) + .visit(v, args...); + s = relaxed_->d.sizes[i]; + } + } else { + auto ss = shift_ - B; + for (auto i = count_t{0}; i < n; ++i) { + visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s, + v, args...); + s = relaxed_->d.sizes[i]; + } + } + } + + template + void each_right_sub(Visitor v, Args&&... args) + { each_right(v, 1, std::forward(args)...); } + + template + void each_right(Visitor v, count_t start, Args&&... args) + { + assert(start > 0); + assert(start <= relaxed_->d.count); + auto s = relaxed_->d.sizes[start - 1]; + auto p = node_->inner(); + if (shift_ == BL) { + for (auto i = start; i < relaxed_->d.count; ++i) { + IMMER_PREFETCH(p + i + 1); + make_leaf_sub_pos(p[i], relaxed_->d.sizes[i] - s) + .visit(v, args...); + s = relaxed_->d.sizes[i]; + } + } else { + auto ss = shift_ - B; + for (auto i = start; i < relaxed_->d.count; ++i) { + visit_maybe_relaxed_sub(p[i], ss, relaxed_->d.sizes[i] - s, + v, args...); + s = relaxed_->d.sizes[i]; + } + } + } + + template + decltype(auto) towards(Visitor v, size_t idx, Args&&... args) + { return towards_oh(v, idx, subindex(idx), args...); } + + template + decltype(auto) towards_oh(Visitor v, size_t idx, + count_t offset_hint, + Args&&... args) + { + assert(offset_hint == index(idx)); + auto left_size = offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0; + return towards_oh_sbh(v, idx, offset_hint, left_size, args...); + } + + template + decltype(auto) towards_oh_sbh(Visitor v, size_t idx, + count_t offset_hint, + size_t left_size_hint, + Args&&... args) + { return towards_sub_oh_sbh(v, idx, offset_hint, left_size_hint, args...); } + + template + decltype(auto) towards_sub_oh(Visitor v, size_t idx, + count_t offset_hint, + Args&&... args) + { + assert(offset_hint == index(idx)); + auto left_size = offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0; + return towards_sub_oh_sbh(v, idx, offset_hint, left_size, args...); + } + + template + decltype(auto) towards_sub_oh_sbh(Visitor v, size_t idx, + count_t offset_hint, + size_t left_size_hint, + Args&&... args) + { + assert(offset_hint == index(idx)); + assert(left_size_hint == + (offset_hint ? relaxed_->d.sizes[offset_hint - 1] : 0)); + auto child = node_->inner() [offset_hint]; + auto is_leaf = shift_ == BL; + auto next_size = relaxed_->d.sizes[offset_hint] - left_size_hint; + auto next_idx = idx - left_size_hint; + return is_leaf + ? make_leaf_sub_pos(child, next_size).visit( + v, next_idx, args...) + : visit_maybe_relaxed_sub(child, shift_ - B, next_size, + v, next_idx, args...); + } + + template + decltype(auto) last_oh_csh(Visitor v, + count_t offset_hint, + size_t child_size_hint, + Args&&... args) + { + assert(offset_hint == count() - 1); + assert(child_size_hint == size(offset_hint)); + auto child = node_->inner() [offset_hint]; + auto is_leaf = shift_ == BL; + return is_leaf + ? make_leaf_sub_pos(child, child_size_hint).visit(v, args...) + : visit_maybe_relaxed_sub(child, shift_ - B, child_size_hint, + v, args...); + } + + template + decltype(auto) last_sub(Visitor v, Args&&... args) + { + auto offset = relaxed_->d.count - 1; + auto child = node_->inner() [offset]; + auto child_size = size(offset); + auto is_leaf = shift_ == BL; + return is_leaf + ? make_leaf_sub_pos(child, child_size).visit(v, args...) + : visit_maybe_relaxed_sub(child, shift_ - B, child_size, v, args...); + } + + template + decltype(auto) first_sub(Visitor v, Args&&... args) + { + auto child = node_->inner() [0]; + auto child_size = relaxed_->d.sizes[0]; + auto is_leaf = shift_ == BL; + return is_leaf + ? make_leaf_sub_pos(child, child_size).visit(v, args...) + : visit_maybe_relaxed_sub(child, shift_ - B, child_size, v, args...); + } + + template + decltype(auto) first_sub_leaf(Visitor v, Args&&... args) + { + assert(shift_ == BL); + auto child = node_->inner() [0]; + auto child_size = relaxed_->d.sizes[0]; + return make_leaf_sub_pos(child, child_size).visit(v, args...); + } + + template + decltype(auto) first_sub_inner(Visitor v, Args&&... args) + { + assert(shift_ > BL); + auto child = node_->inner() [0]; + auto child_size = relaxed_->d.sizes[0]; + return visit_maybe_relaxed_sub(child, shift_ - B, child_size, v, args...); + } + + template + decltype(auto) nth_sub(count_t offset, Visitor v, Args&&... args) + { + auto child = node_->inner() [offset]; + auto child_size = size(offset); + auto is_leaf = shift_ == BL; + return is_leaf + ? make_leaf_sub_pos(child, child_size).visit(v, args...) + : visit_maybe_relaxed_sub(child, shift_ - B, child_size, v, args...); + } + + template + decltype(auto) nth_sub_leaf(count_t offset, Visitor v, Args&&... args) + { + assert(shift_ == BL); + auto child = node_->inner() [offset]; + auto child_size = size(offset); + return make_leaf_sub_pos(child, child_size).visit(v, args...); + } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_relaxed(v, *this, std::forward(args)...); + } +}; + +template +using is_relaxed = std::is_same::node_t>, + std::decay_t>; + +template +constexpr auto is_relaxed_v = is_relaxed::value; + +template +relaxed_pos make_relaxed_pos(NodeT* node, + shift_t shift, + typename NodeT::relaxed_t* relaxed) +{ + assert(node); + assert(relaxed); + assert(shift >= NodeT::bits_leaf); + return {node, shift, relaxed}; +} + +template +decltype(auto) visit_maybe_relaxed_sub(NodeT* node, shift_t shift, size_t size, + Visitor v, Args&& ...args) +{ + assert(node); + auto relaxed = node->relaxed(); + if (relaxed) { + assert(size == relaxed->d.sizes[relaxed->d.count - 1]); + return make_relaxed_pos(node, shift, relaxed) + .visit(v, std::forward(args)...); + } else { + return make_regular_sub_pos(node, shift, size) + .visit(v, std::forward(args)...); + } +} + +template +struct relaxed_descent_pos +{ + static_assert(Shift > 0, "not leaf..."); + + using node_t = NodeT; + using relaxed_t = typename NodeT::relaxed_t; + node_t* node_; + relaxed_t* relaxed_; + + count_t count() const { return relaxed_->d.count; } + node_t* node() const { return node_; } + shift_t shift() const { return Shift; } + size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; } + + count_t index(size_t idx) const + { + // make gcc happy +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshift-count-overflow" + auto offset = idx >> Shift; +#pragma GCC diagnostic pop + while (relaxed_->d.sizes[offset] <= idx) ++offset; + return offset; + } + + template + decltype(auto) descend(Visitor v, size_t idx) + { + auto offset = index(idx); + auto child = node_->inner() [offset]; + auto left_size = offset ? relaxed_->d.sizes[offset - 1] : 0; + auto next_idx = idx - left_size; + auto r = child->relaxed(); + return r + ? relaxed_descent_pos{child, r}.visit(v, next_idx) + : regular_descent_pos{child}.visit(v, next_idx); + } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_relaxed(v, *this, std::forward(args)...); + } +}; + +template +struct relaxed_descent_pos +{ + using node_t = NodeT; + using relaxed_t = typename NodeT::relaxed_t; + node_t* node_; + relaxed_t* relaxed_; + + count_t count() const { return relaxed_->d.count; } + node_t* node() const { return node_; } + shift_t shift() const { return BL; } + size_t size() const { return relaxed_->d.sizes[relaxed_->d.count - 1]; } + + count_t index(size_t idx) const + { + auto offset = (idx >> BL) & mask; + while (relaxed_->d.sizes[offset] <= idx) ++offset; + return offset; + } + + template + decltype(auto) descend(Visitor v, size_t idx) + { + auto offset = index(idx); + auto child = node_->inner() [offset]; + auto left_size = offset ? relaxed_->d.sizes[offset - 1] : 0; + auto next_idx = idx - left_size; + return leaf_descent_pos{child}.visit(v, next_idx); + } + + template + decltype(auto) visit(Visitor v, Args&& ...args) + { + return visit_relaxed(v, *this, std::forward(args)...); + } +}; + +template +decltype(auto) visit_maybe_relaxed_descent(NodeT* node, shift_t shift, + Visitor v, size_t idx) +{ + constexpr auto B = NodeT::bits; + constexpr auto BL = NodeT::bits_leaf; + assert(node); + assert(shift >= BL); + auto r = node->relaxed(); + if (r) { + switch (shift) { + case BL + B * 0: return relaxed_descent_pos{node, r}.visit(v, idx); + case BL + B * 1: return relaxed_descent_pos{node, r}.visit(v, idx); + case BL + B * 2: return relaxed_descent_pos{node, r}.visit(v, idx); + case BL + B * 3: return relaxed_descent_pos{node, r}.visit(v, idx); + case BL + B * 4: return relaxed_descent_pos{node, r}.visit(v, idx); + case BL + B * 5: return relaxed_descent_pos{node, r}.visit(v, idx); +#if IMMER_DESCENT_DEEP + default: + for (auto level = shift; level != endshift; level -= B) { + auto r = node->relaxed(); + if (r) { + auto node_idx = (idx >> level) & mask; + while (r->d.sizes[node_idx] <= idx) ++node_idx; + if (node_idx) idx -= r->d.sizes[node_idx - 1]; + node = node->inner() [node_idx]; + } else { + do { + node = node->inner() [(idx >> level) & mask]; + } while ((level -= B) != endshift); + return make_leaf_descent_pos(node).visit(v, idx); + } + } + return make_leaf_descent_pos(node).visit(v, idx); +#endif // IMMER_DESCENT_DEEP + } + IMMER_UNREACHABLE; + } else { + return visit_regular_descent(node, shift, v, idx); + } +} + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/rbtree.hpp b/src/immer/detail/rbts/rbtree.hpp new file mode 100644 index 000000000000..d4f3bcf9a01a --- /dev/null +++ b/src/immer/detail/rbts/rbtree.hpp @@ -0,0 +1,528 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace immer { +namespace detail { +namespace rbts { + +template +struct rbtree +{ + using node_t = node; + using edit_t = typename node_t::edit_t; + using owner_t = typename MemoryPolicy::transience_t::owner; + + size_t size; + shift_t shift; + node_t* root; + node_t* tail; + + static const rbtree empty; + + template + static auto from_initializer_list(std::initializer_list values) + { + auto e = owner_t{}; + auto result = rbtree{empty}; + for (auto&& v : values) + result.push_back_mut(e, v); + return result; + } + + template + static auto from_range(Iter first, Iter last) + { + auto e = owner_t{}; + auto result = rbtree{empty}; + for (; first != last; ++first) + result.push_back_mut(e, *first); + return result; + } + + static auto from_fill(size_t n, T v) + { + auto e = owner_t{}; + auto result = rbtree{empty}; + while (n --> 0) + result.push_back_mut(e, v); + return result; + } + + rbtree(size_t sz, shift_t sh, node_t* r, node_t* t) + : size{sz}, shift{sh}, root{r}, tail{t} + { + assert(check_tree()); + } + + rbtree(const rbtree& other) + : rbtree{other.size, other.shift, other.root, other.tail} + { + inc(); + } + + rbtree(rbtree&& other) + : rbtree{empty} + { + swap(*this, other); + } + + rbtree& operator=(const rbtree& other) + { + auto next = other; + swap(*this, next); + return *this; + } + + rbtree& operator=(rbtree&& other) + { + swap(*this, other); + return *this; + } + + friend void swap(rbtree& x, rbtree& y) + { + using std::swap; + swap(x.size, y.size); + swap(x.shift, y.shift); + swap(x.root, y.root); + swap(x.tail, y.tail); + } + + ~rbtree() + { + dec(); + } + + void inc() const + { + root->inc(); + tail->inc(); + } + + void dec() const + { + traverse(dec_visitor()); + } + + auto tail_size() const + { + return size ? ((size - 1) & mask) + 1 : 0; + } + + auto tail_offset() const + { + return size ? (size - 1) & ~mask : 0; + } + + template + void traverse(Visitor v, Args&&... args) const + { + auto tail_off = tail_offset(); + auto tail_size = size - tail_off; + + if (tail_off) make_regular_sub_pos(root, shift, tail_off).visit(v, args...); + else make_empty_regular_pos(root).visit(v, args...); + + make_leaf_sub_pos(tail, tail_size).visit(v, args...); + } + + template + void traverse(Visitor v, size_t first, size_t last, Args&&... args) const + { + auto tail_off = tail_offset(); + auto tail_size = size - tail_off; + + if (first < tail_off) + make_regular_sub_pos(root, shift, tail_off).visit( + v, + first, + last < tail_off ? last : tail_off, + args...); + if (last > tail_off) + make_leaf_sub_pos(tail, tail_size).visit( + v, + first > tail_off ? first - tail_off : 0, + last - tail_off, + args...); + } + + template + bool traverse_p(Visitor v, Args&&... args) const + { + auto tail_off = tail_offset(); + auto tail_size = size - tail_off; + return (tail_off + ? make_regular_sub_pos(root, shift, tail_off).visit(v, args...) + : make_empty_regular_pos(root).visit(v, args...)) + && make_leaf_sub_pos(tail, tail_size).visit(v, args...); + } + + template + bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const + { + auto tail_off = tail_offset(); + auto tail_size = size - tail_off; + + return + (first < tail_off + ? make_regular_sub_pos(root, shift, tail_off).visit( + v, + first, + last < tail_off ? last : tail_off, + args...) + : true) + && (last > tail_off + ? make_leaf_sub_pos(tail, tail_size).visit( + v, + first > tail_off ? first - tail_off : 0, + last - tail_off, + args...) + : true); + } + + template + decltype(auto) descend(Visitor v, size_t idx) const + { + auto tail_off = tail_offset(); + return idx >= tail_off + ? make_leaf_descent_pos(tail).visit(v, idx) + : visit_regular_descent(root, shift, v, idx); + } + + template + void for_each_chunk(Fn&& fn) const + { + traverse(for_each_chunk_visitor{}, std::forward(fn)); + } + + template + void for_each_chunk(size_t first, size_t last, Fn&& fn) const + { + traverse(for_each_chunk_i_visitor{}, first, last, std::forward(fn)); + } + + template + bool for_each_chunk_p(Fn&& fn) const + { + return traverse_p(for_each_chunk_p_visitor{}, std::forward(fn)); + } + + template + bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const + { + return traverse_p(for_each_chunk_p_i_visitor{}, first, last, std::forward(fn)); + } + + bool equals(const rbtree& other) const + { + if (size != other.size) return false; + if (size == 0) return true; + return (size <= branches + || make_regular_sub_pos(root, shift, tail_offset()).visit( + equals_visitor{}, other.root)) + && make_leaf_sub_pos(tail, tail_size()).visit( + equals_visitor{}, other.tail); + } + + void ensure_mutable_tail(edit_t e, count_t n) + { + if (!tail->can_mutate(e)) { + auto new_tail = node_t::copy_leaf_e(e, tail, n); + dec_leaf(tail, n); + tail = new_tail; + } + } + + void push_back_mut(edit_t e, T value) + { + auto tail_off = tail_offset(); + auto ts = size - tail_off; + if (ts < branches) { + ensure_mutable_tail(e, ts); + new (&tail->leaf()[ts]) T{std::move(value)}; + } else { + auto new_tail = node_t::make_leaf_e(e, std::move(value)); + try { + if (tail_off == size_t{branches} << shift) { + auto new_root = node_t::make_inner_e(e); + try { + auto path = node_t::make_path_e(e, shift, tail); + new_root->inner() [0] = root; + new_root->inner() [1] = path; + root = new_root; + tail = new_tail; + shift += B; + } catch (...) { + node_t::delete_inner_e(new_root); + throw; + } + } else if (tail_off) { + auto new_root = make_regular_sub_pos(root, shift, tail_off) + .visit(push_tail_mut_visitor{}, e, tail); + root = new_root; + tail = new_tail; + } else { + auto new_root = node_t::make_path_e(e, shift, tail); + assert(tail_off == 0); + dec_empty_regular(root); + root = new_root; + tail = new_tail; + } + } catch (...) { + node_t::delete_leaf(new_tail, 1); + throw; + } + } + ++size; + } + + rbtree push_back(T value) const + { + auto tail_off = tail_offset(); + auto ts = size - tail_off; + if (ts < branches) { + auto new_tail = node_t::copy_leaf_emplace(tail, ts, + std::move(value)); + return { size + 1, shift, root->inc(), new_tail }; + } else { + auto new_tail = node_t::make_leaf_n(1, std::move(value)); + try { + if (tail_off == size_t{branches} << shift) { + auto new_root = node_t::make_inner_n(2); + try { + auto path = node_t::make_path(shift, tail); + new_root->inner() [0] = root; + new_root->inner() [1] = path; + root->inc(); + tail->inc(); + return { size + 1, shift + B, new_root, new_tail }; + } catch (...) { + node_t::delete_inner(new_root, 2); + throw; + } + } else if (tail_off) { + auto new_root = make_regular_sub_pos(root, shift, tail_off) + .visit(push_tail_visitor{}, tail); + tail->inc(); + return { size + 1, shift, new_root, new_tail }; + } else { + auto new_root = node_t::make_path(shift, tail); + tail->inc(); + return { size + 1, shift, new_root, new_tail }; + } + } catch (...) { + node_t::delete_leaf(new_tail, 1); + throw; + } + } + } + + const T* array_for(size_t index) const + { + return descend(array_for_visitor(), index); + } + + T& get_mut(edit_t e, size_t idx) + { + auto tail_off = tail_offset(); + if (idx >= tail_off) { + ensure_mutable_tail(e, size - tail_off); + return tail->leaf() [idx & mask]; + } else { + return make_regular_sub_pos(root, shift, tail_off) + .visit(get_mut_visitor{}, idx, e, &root); + } + } + + const T& get(size_t index) const + { + return descend(get_visitor(), index); + } + + const T& get_check(size_t index) const + { + if (index >= size) + throw std::out_of_range{"index out of range"}; + return descend(get_visitor(), index); + } + + const T& front() const + { + return get(0); + } + + const T& back() const + { + return tail->leaf()[(size - 1) & mask]; + } + + template + void update_mut(edit_t e, size_t idx, FnT&& fn) + { + auto& elem = get_mut(e, idx); + elem = std::forward(fn) (std::move(elem)); + } + + template + rbtree update(size_t idx, FnT&& fn) const + { + auto tail_off = tail_offset(); + if (idx >= tail_off) { + auto tail_size = size - tail_off; + auto new_tail = make_leaf_sub_pos(tail, tail_size) + .visit(update_visitor{}, idx - tail_off, fn); + return { size, shift, root->inc(), new_tail }; + } else { + auto new_root = make_regular_sub_pos(root, shift, tail_off) + .visit(update_visitor{}, idx, fn); + return { size, shift, new_root, tail->inc() }; + } + } + + void assoc_mut(edit_t e, size_t idx, T value) + { + update_mut(e, idx, [&] (auto&&) { + return std::move(value); + }); + } + + rbtree assoc(size_t idx, T value) const + { + return update(idx, [&] (auto&&) { + return std::move(value); + }); + } + + rbtree take(size_t new_size) const + { + auto tail_off = tail_offset(); + if (new_size == 0) { + return empty; + } else if (new_size >= size) { + return *this; + } else if (new_size > tail_off) { + auto new_tail = node_t::copy_leaf(tail, new_size - tail_off); + return { new_size, shift, root->inc(), new_tail }; + } else { + using std::get; + auto l = new_size - 1; + auto v = slice_right_visitor(); + auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l); + auto new_shift = get<0>(r); + auto new_root = get<1>(r); + auto new_tail = get<3>(r); + if (new_root) { + assert(new_root->compute_shift() == get<0>(r)); + assert(new_root->check(new_shift, new_size - get<2>(r))); + return { new_size, new_shift, new_root, new_tail }; + } else { + return { new_size, BL, empty.root->inc(), new_tail }; + } + } + } + + void take_mut(edit_t e, size_t new_size) + { + auto tail_off = tail_offset(); + if (new_size == 0) { + // todo: more efficient? + *this = empty; + } else if (new_size >= size) { + return; + } else if (new_size > tail_off) { + auto ts = size - tail_off; + auto newts = new_size - tail_off; + if (tail->can_mutate(e)) { + destroy_n(tail->leaf() + newts, ts - newts); + } else { + auto new_tail = node_t::copy_leaf_e(e, tail, newts); + dec_leaf(tail, ts); + tail = new_tail; + } + size = new_size; + return; + } else { + using std::get; + auto l = new_size - 1; + auto v = slice_right_mut_visitor(); + auto r = make_regular_sub_pos(root, shift, tail_off).visit(v, l, e); + auto new_shift = get<0>(r); + auto new_root = get<1>(r); + auto new_tail = get<3>(r); + if (new_root) { + root = new_root; + shift = new_shift; + } else { + root = empty.root->inc(); + shift = BL; + } + dec_leaf(tail, size - tail_off); + size = new_size; + tail = new_tail; + return; + } + } + + bool check_tree() const + { +#if IMMER_DEBUG_DEEP_CHECK + assert(shift >= BL); + assert(tail_offset() <= size); + assert(check_root()); + assert(check_tail()); +#endif + return true; + } + + bool check_tail() const + { +#if IMMER_DEBUG_DEEP_CHECK + if (tail_size() > 0) + assert(tail->check(0, tail_size())); +#endif + return true; + } + + bool check_root() const + { +#if IMMER_DEBUG_DEEP_CHECK + if (tail_offset() > 0) + assert(root->check(shift, tail_offset())); + else { + assert(root->kind() == node_t::kind_t::inner); + assert(shift == BL); + } +#endif + return true; + } +}; + +template +const rbtree rbtree::empty = { + 0, + BL, + node_t::make_inner_n(0), + node_t::make_leaf_n(0) +}; + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/rbtree_iterator.hpp b/src/immer/detail/rbts/rbtree_iterator.hpp new file mode 100644 index 000000000000..f1890dc053a1 --- /dev/null +++ b/src/immer/detail/rbts/rbtree_iterator.hpp @@ -0,0 +1,100 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { +namespace detail { +namespace rbts { + +template +struct rbtree_iterator + : iterator_facade, + std::random_access_iterator_tag, + T, + const T&> +{ + using tree_t = rbtree; + + struct end_t {}; + + rbtree_iterator() = default; + + rbtree_iterator(const tree_t& v) + : v_ { &v } + , i_ { 0 } + , base_ { ~size_t{} } + , curr_ { nullptr } + {} + + rbtree_iterator(const tree_t& v, end_t) + : v_ { &v } + , i_ { v.size } + , base_ { ~size_t{} } + , curr_ { nullptr } + {} + + const tree_t& impl() const { return *v_; } + size_t index() const { return i_; } + +private: + friend iterator_core_access; + + const tree_t* v_; + size_t i_; + mutable size_t base_; + mutable const T* curr_ = nullptr; + + void increment() + { + assert(i_ < v_->size); + ++i_; + } + + void decrement() + { + assert(i_ > 0); + --i_; + } + + void advance(std::ptrdiff_t n) + { + assert(n <= 0 || i_ + static_cast(n) <= v_->size); + assert(n >= 0 || static_cast(-n) <= i_); + i_ += n; + } + + bool equal(const rbtree_iterator& other) const + { + return i_ == other.i_; + } + + std::ptrdiff_t distance_to(const rbtree_iterator& other) const + { + return other.i_ > i_ + ? static_cast(other.i_ - i_) + : - static_cast(i_ - other.i_); + } + + const T& dereference() const + { + auto base = i_ & ~mask; + if (base_ != base) { + base_ = base; + curr_ = v_->array_for(i_); + } + return curr_[i_ & mask]; + } +}; + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/rrbtree.hpp b/src/immer/detail/rbts/rrbtree.hpp new file mode 100644 index 000000000000..5766cffc50b7 --- /dev/null +++ b/src/immer/detail/rbts/rrbtree.hpp @@ -0,0 +1,1272 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace immer { +namespace detail { +namespace rbts { + +template +struct rrbtree_iterator; + +template +struct rrbtree +{ + using node_t = node; + using edit_t = typename node_t::edit_t; + using owner_t = typename MemoryPolicy::transience_t::owner; + + size_t size; + shift_t shift; + node_t* root; + node_t* tail; + + static const rrbtree empty; + + template + static auto from_initializer_list(std::initializer_list values) + { + auto e = owner_t{}; + auto result = rrbtree{empty}; + for (auto&& v : values) + result.push_back_mut(e, v); + return result; + } + + template + static auto from_range(Iter first, Iter last) + { + auto e = owner_t{}; + auto result = rrbtree{empty}; + for (; first != last; ++first) + result.push_back_mut(e, *first); + return result; + } + + static auto from_fill(size_t n, T v) + { + auto e = owner_t{}; + auto result = rrbtree{empty}; + while (n --> 0) + result.push_back_mut(e, v); + return result; + } + + rrbtree(size_t sz, shift_t sh, node_t* r, node_t* t) + : size{sz}, shift{sh}, root{r}, tail{t} + { + assert(check_tree()); + } + + rrbtree(const rrbtree& other) + : rrbtree{other.size, other.shift, other.root, other.tail} + { + inc(); + } + + rrbtree(rrbtree&& other) + : rrbtree{empty} + { + swap(*this, other); + } + + rrbtree& operator=(const rrbtree& other) + { + auto next{other}; + swap(*this, next); + return *this; + } + + rrbtree& operator=(rrbtree&& other) + { + swap(*this, other); + return *this; + } + + friend void swap(rrbtree& x, rrbtree& y) + { + using std::swap; + swap(x.size, y.size); + swap(x.shift, y.shift); + swap(x.root, y.root); + swap(x.tail, y.tail); + } + + ~rrbtree() + { + dec(); + } + + void inc() const + { + root->inc(); + tail->inc(); + } + + void dec() const + { + traverse(dec_visitor()); + } + + auto tail_size() const + { + return size - tail_offset(); + } + + auto tail_offset() const + { + auto r = root->relaxed(); + assert(r == nullptr || r->d.count); + return + r ? r->d.sizes[r->d.count - 1] : + size ? (size - 1) & ~mask + /* otherwise */ : 0; + } + + template + void traverse(Visitor v, Args&&... args) const + { + auto tail_off = tail_offset(); + auto tail_size = size - tail_off; + + if (tail_off) visit_maybe_relaxed_sub(root, shift, tail_off, v, args...); + else make_empty_regular_pos(root).visit(v, args...); + + if (tail_size) make_leaf_sub_pos(tail, tail_size).visit(v, args...); + else make_empty_leaf_pos(tail).visit(v, args...); + } + + template + void traverse(Visitor v, size_t first, size_t last, Args&&... args) const + { + auto tail_off = tail_offset(); + auto tail_size = size - tail_off; + + if (first < tail_off) + visit_maybe_relaxed_sub(root, shift, tail_off, v, + first, + last < tail_off ? last : tail_off, + args...); + if (last > tail_off) + make_leaf_sub_pos(tail, tail_size).visit( + v, + first > tail_off ? first - tail_off : 0, + last - tail_off, + args...); + } + + template + bool traverse_p(Visitor v, Args&&... args) const + { + auto tail_off = tail_offset(); + auto tail_size = size - tail_off; + return (tail_off + ? visit_maybe_relaxed_sub(root, shift, tail_off, v, args...) + : make_empty_regular_pos(root).visit(v, args...)) + && (tail_size + ? make_leaf_sub_pos(tail, tail_size).visit(v, args...) + : make_empty_leaf_pos(tail).visit(v, args...)); + } + + template + bool traverse_p(Visitor v, size_t first, size_t last, Args&&... args) const + { + auto tail_off = tail_offset(); + auto tail_size = size - tail_off; + return + (first < tail_off + ? visit_maybe_relaxed_sub(root, shift, tail_off, v, + first, + last < tail_off ? last : tail_off, + args...) + : true) + && (last > tail_off + ? make_leaf_sub_pos(tail, tail_size).visit( + v, + first > tail_off ? first - tail_off : 0, + last - tail_off, + args...) + : true); + } + + template + decltype(auto) descend(Visitor v, size_t idx) const + { + auto tail_off = tail_offset(); + return idx >= tail_off + ? make_leaf_descent_pos(tail).visit(v, idx - tail_off) + : visit_maybe_relaxed_descent(root, shift, v, idx); + } + + template + void for_each_chunk(Fn&& fn) const + { + traverse(for_each_chunk_visitor{}, std::forward(fn)); + } + + template + void for_each_chunk(size_t first, size_t last, Fn&& fn) const + { + traverse(for_each_chunk_i_visitor{}, first, last, std::forward(fn)); + } + + template + bool for_each_chunk_p(Fn&& fn) const + { + return traverse_p(for_each_chunk_p_visitor{}, std::forward(fn)); + } + + template + bool for_each_chunk_p(size_t first, size_t last, Fn&& fn) const + { + return traverse_p(for_each_chunk_p_i_visitor{}, first, last, std::forward(fn)); + } + + bool equals(const rrbtree& other) const + { + using iter_t = rrbtree_iterator; + if (size != other.size) return false; + if (size == 0) return true; + auto tail_off = tail_offset(); + auto tail_off_other = other.tail_offset(); + // compare trees + if (tail_off > 0 && tail_off_other > 0) { + // other.shift != shift is a theoretical possibility for + // relaxed trees that sadly we haven't managed to exercise + // in tests yet... + if (other.shift >= shift) { + if (!visit_maybe_relaxed_sub( + other.root, other.shift, tail_off_other, + equals_visitor::rrb{}, iter_t{other}, + root, shift, tail_off)) + return false; + } else { + if (!visit_maybe_relaxed_sub( + root, shift, tail_off, + equals_visitor::rrb{}, iter_t{*this}, + other.root, other.shift, tail_off_other)) + return false; + } + } + return + tail_off == tail_off_other ? make_leaf_sub_pos( + tail, tail_size()).visit( + equals_visitor{}, other.tail) : + tail_off > tail_off_other ? std::equal( + tail->leaf(), tail->leaf() + (size - tail_off), + other.tail->leaf() + (tail_off - tail_off_other)) + /* otherwise */ : std::equal( + tail->leaf(), tail->leaf() + (size - tail_off), + iter_t{other} + tail_off); + } + + std::tuple + push_tail(node_t* root, shift_t shift, size_t size, + node_t* tail, count_t tail_size) const + { + if (auto r = root->relaxed()) { + auto new_root = make_relaxed_pos(root, shift, r) + .visit(push_tail_visitor{}, tail, tail_size); + if (new_root) + return { shift, new_root }; + else { + auto new_root = node_t::make_inner_r_n(2); + try { + auto new_path = node_t::make_path(shift, tail); + new_root->inner() [0] = root->inc(); + new_root->inner() [1] = new_path; + new_root->relaxed()->d.sizes [0] = size; + new_root->relaxed()->d.sizes [1] = size + tail_size; + new_root->relaxed()->d.count = 2u; + } catch (...) { + node_t::delete_inner_r(new_root, 2); + throw; + } + return { shift + B, new_root }; + } + } else if (size == size_t{branches} << shift) { + auto new_root = node_t::make_inner_n(2); + try { + auto new_path = node_t::make_path(shift, tail); + new_root->inner() [0] = root->inc(); + new_root->inner() [1] = new_path; + } catch (...) { + node_t::delete_inner(new_root, 2); + throw; + } + return { shift + B, new_root }; + } else if (size) { + auto new_root = make_regular_sub_pos(root, shift, size) + .visit(push_tail_visitor{}, tail); + return { shift, new_root }; + } else { + return { shift, node_t::make_path(shift, tail) }; + } + } + + void push_tail_mut(edit_t e, size_t tail_off, + node_t* tail, count_t tail_size) + { + if (auto r = root->relaxed()) { + auto new_root = make_relaxed_pos(root, shift, r) + .visit(push_tail_mut_visitor{}, e, tail, tail_size); + if (new_root) { + root = new_root; + } else { + auto new_root = node_t::make_inner_r_e(e); + try { + auto new_path = node_t::make_path_e(e, shift, tail); + new_root->inner() [0] = root; + new_root->inner() [1] = new_path; + new_root->relaxed()->d.sizes [0] = tail_off; + new_root->relaxed()->d.sizes [1] = tail_off + tail_size; + new_root->relaxed()->d.count = 2u; + root = new_root; + shift += B; + } catch (...) { + node_t::delete_inner_r_e(new_root); + throw; + } + } + } else if (tail_off == size_t{branches} << shift) { + auto new_root = node_t::make_inner_e(e); + try { + auto new_path = node_t::make_path_e(e, shift, tail); + new_root->inner() [0] = root; + new_root->inner() [1] = new_path; + root = new_root; + shift += B; + } catch (...) { + node_t::delete_inner_e(new_root); + throw; + } + } else if (tail_off) { + auto new_root = make_regular_sub_pos(root, shift, tail_off) + .visit(push_tail_mut_visitor{}, e, tail); + root = new_root; + } else { + auto new_root = node_t::make_path_e(e, shift, tail); + dec_empty_regular(root); + root = new_root; + } + } + + void ensure_mutable_tail(edit_t e, count_t n) + { + if (!tail->can_mutate(e)) { + auto new_tail = node_t::copy_leaf_e(e, tail, n); + dec_leaf(tail, n); + tail = new_tail; + } + } + + void push_back_mut(edit_t e, T value) + { + auto ts = tail_size(); + if (ts < branches) { + ensure_mutable_tail(e, ts); + new (&tail->leaf()[ts]) T{std::move(value)}; + } else { + using std::get; + auto new_tail = node_t::make_leaf_e(e, std::move(value)); + auto tail_off = tail_offset(); + try { + push_tail_mut(e, tail_off, tail, ts); + tail = new_tail; + } catch (...) { + node_t::delete_leaf(new_tail, 1u); + throw; + } + } + ++size; + } + + rrbtree push_back(T value) const + { + auto ts = tail_size(); + if (ts < branches) { + auto new_tail = node_t::copy_leaf_emplace(tail, ts, + std::move(value)); + return { size + 1, shift, root->inc(), new_tail }; + } else { + using std::get; + auto new_tail = node_t::make_leaf_n(1u, std::move(value)); + auto tail_off = tail_offset(); + try { + auto new_root = push_tail(root, shift, tail_off, + tail, size - tail_off); + tail->inc(); + return { size + 1, get<0>(new_root), get<1>(new_root), new_tail }; + } catch (...) { + node_t::delete_leaf(new_tail, 1u); + throw; + } + } + } + + std::tuple + region_for(size_t idx) const + { + using std::get; + auto tail_off = tail_offset(); + if (idx >= tail_off) { + return { tail->leaf(), tail_off, size }; + } else { + auto subs = visit_maybe_relaxed_sub( + root, shift, tail_off, + region_for_visitor(), idx); + auto first = idx - get<1>(subs); + auto end = first + get<2>(subs); + return { get<0>(subs), first, end }; + } + } + + T& get_mut(edit_t e, size_t idx) + { + auto tail_off = tail_offset(); + if (idx >= tail_off) { + ensure_mutable_tail(e, size - tail_off); + return tail->leaf() [(idx - tail_off) & mask]; + } else { + return visit_maybe_relaxed_sub( + root, shift, tail_off, + get_mut_visitor{}, idx, e, &root); + } + } + + const T& get(size_t index) const + { + return descend(get_visitor(), index); + } + + const T& get_check(size_t index) const + { + if (index >= size) + throw std::out_of_range{"out of range"}; + return descend(get_visitor(), index); + } + + const T& front() const + { + return get(0); + } + + const T& back() const + { + return get(size - 1); + } + + template + void update_mut(edit_t e, size_t idx, FnT&& fn) + { + auto& elem = get_mut(e, idx); + elem = std::forward(fn) (std::move(elem)); + } + + template + rrbtree update(size_t idx, FnT&& fn) const + { + auto tail_off = tail_offset(); + if (idx >= tail_off) { + auto tail_size = size - tail_off; + auto new_tail = make_leaf_sub_pos(tail, tail_size) + .visit(update_visitor{}, idx - tail_off, fn); + return { size, shift, root->inc(), new_tail }; + } else { + auto new_root = visit_maybe_relaxed_sub( + root, shift, tail_off, + update_visitor{}, idx, fn); + return { size, shift, new_root, tail->inc() }; + } + } + + void assoc_mut(edit_t e, size_t idx, T value) + { + update_mut(e, idx, [&] (auto&&) { + return std::move(value); + }); + } + + rrbtree assoc(size_t idx, T value) const + { + return update(idx, [&] (auto&&) { + return std::move(value); + }); + } + + void take_mut(edit_t e, size_t new_size) + { + auto tail_off = tail_offset(); + if (new_size == 0) { + *this = empty; + } else if (new_size >= size) { + return; + } else if (new_size > tail_off) { + auto ts = size - tail_off; + auto newts = new_size - tail_off; + if (tail->can_mutate(e)) { + destroy_n(tail->leaf() + newts, ts - newts); + } else { + auto new_tail = node_t::copy_leaf_e(e, tail, newts); + dec_leaf(tail, ts); + tail = new_tail; + } + size = new_size; + return; + } else { + using std::get; + auto l = new_size - 1; + auto v = slice_right_mut_visitor(); + auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l, e); + auto new_shift = get<0>(r); + auto new_root = get<1>(r); + auto new_tail = get<3>(r); + if (new_root) { + root = new_root; + shift = new_shift; + } else { + root = empty.root->inc(); + shift = BL; + } + dec_leaf(tail, size - tail_off); + size = new_size; + tail = new_tail; + return; + } + } + + rrbtree take(size_t new_size) const + { + auto tail_off = tail_offset(); + if (new_size == 0) { + return empty; + } else if (new_size >= size) { + return *this; + } else if (new_size > tail_off) { + auto new_tail = node_t::copy_leaf(tail, new_size - tail_off); + return { new_size, shift, root->inc(), new_tail }; + } else { + using std::get; + auto l = new_size - 1; + auto v = slice_right_visitor(); + auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, l); + auto new_shift = get<0>(r); + auto new_root = get<1>(r); + auto new_tail = get<3>(r); + if (new_root) { + assert(new_root->compute_shift() == get<0>(r)); + assert(new_root->check(new_shift, new_size - get<2>(r))); + return { new_size, new_shift, new_root, new_tail }; + } else { + return { new_size, BL, empty.root->inc(), new_tail }; + } + } + } + + void drop_mut(edit_t e, size_t elems) + { + using std::get; + auto tail_off = tail_offset(); + if (elems == 0) { + return; + } else if (elems >= size) { + *this = empty; + } else if (elems == tail_off) { + dec_inner(root, shift, tail_off); + shift = BL; + root = empty.root->inc(); + size -= elems; + return; + } else if (elems > tail_off) { + auto v = slice_left_mut_visitor(); + tail = get<1>(make_leaf_sub_pos(tail, size - tail_off).visit( + v, elems - tail_off, e)); + if (root != empty.root) { + dec_inner(root, shift, tail_off); + shift = BL; + root = empty.root->inc(); + } + size -= elems; + return; + } else { + auto v = slice_left_mut_visitor(); + auto r = visit_maybe_relaxed_sub(root, shift, tail_off, v, elems, e); + shift = get<0>(r); + root = get<1>(r); + size -= elems; + return; + } + } + + rrbtree drop(size_t elems) const + { + if (elems == 0) { + return *this; + } else if (elems >= size) { + return empty; + } else if (elems == tail_offset()) { + return { size - elems, BL, empty.root->inc(), tail->inc() }; + } else if (elems > tail_offset()) { + auto tail_off = tail_offset(); + auto new_tail = node_t::copy_leaf(tail, elems - tail_off, + size - tail_off); + return { size - elems, BL, empty.root->inc(), new_tail }; + } else { + using std::get; + auto v = slice_left_visitor(); + auto r = visit_maybe_relaxed_sub(root, shift, tail_offset(), v, elems); + auto new_root = get<1>(r); + auto new_shift = get<0>(r); + return { size - elems, new_shift, new_root, tail->inc() }; + } + return *this; + } + + rrbtree concat(const rrbtree& r) const + { + assert(r.size < (std::numeric_limits::max() - size)); + using std::get; + if (size == 0) + return r; + else if (r.size == 0) + return *this; + else if (r.tail_offset() == 0) { + // just concat the tail, similar to push_back + auto tail_offst = tail_offset(); + auto tail_size = size - tail_offst; + if (tail_size == branches) { + auto new_root = push_tail(root, shift, tail_offst, + tail, tail_size); + tail->inc(); + return { size + r.size, get<0>(new_root), get<1>(new_root), + r.tail->inc() }; + } else if (tail_size + r.size <= branches) { + auto new_tail = node_t::copy_leaf(tail, tail_size, + r.tail, r.size); + return { size + r.size, shift, root->inc(), new_tail }; + } else { + auto remaining = branches - tail_size; + auto add_tail = node_t::copy_leaf(tail, tail_size, + r.tail, remaining); + try { + auto new_tail = node_t::copy_leaf(r.tail, remaining, r.size); + try { + auto new_root = push_tail(root, shift, tail_offst, + add_tail, branches); + return { size + r.size, + get<0>(new_root), get<1>(new_root), + new_tail }; + } catch (...) { + node_t::delete_leaf(new_tail, r.size - remaining); + throw; + } + } catch (...) { + node_t::delete_leaf(add_tail, branches); + throw; + } + } + } else if (tail_offset() == 0) { + auto tail_offst = tail_offset(); + auto tail_size = size - tail_offst; + auto concated = concat_trees(tail, tail_size, + r.root, r.shift, r.tail_offset()); + auto new_shift = concated.shift(); + auto new_root = concated.node(); + assert(new_shift == new_root->compute_shift()); + assert(new_root->check(new_shift, size + r.tail_offset())); + return { size + r.size, new_shift, new_root, r.tail->inc() }; + } else { + auto tail_offst = tail_offset(); + auto tail_size = size - tail_offst; + auto concated = concat_trees(root, shift, tail_offst, + tail, tail_size, + r.root, r.shift, r.tail_offset()); + auto new_shift = concated.shift(); + auto new_root = concated.node(); + assert(new_shift == new_root->compute_shift()); + assert(new_root->check(new_shift, size + r.tail_offset())); + return { size + r.size, new_shift, new_root, r.tail->inc() }; + } + } + + constexpr static bool supports_transient_concat = + !std::is_empty::value; + + friend void concat_mut_l(rrbtree& l, edit_t el, const rrbtree& r) + { + assert(&l != &r); + assert(r.size < (std::numeric_limits::max() - l.size)); + using std::get; + if (l.size == 0) + l = r; + else if (r.size == 0) + return; + else if (r.tail_offset() == 0) { + // just concat the tail, similar to push_back + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + if (tail_size == branches) { + l.push_tail_mut(el, tail_offst, l.tail, tail_size); + l.tail = r.tail->inc(); + l.size += r.size; + return; + } else if (tail_size + r.size <= branches) { + l.ensure_mutable_tail(el, tail_size); + std::uninitialized_copy(r.tail->leaf(), + r.tail->leaf() + r.size, + l.tail->leaf() + tail_size); + l.size += r.size; + return; + } else { + auto remaining = branches - tail_size; + l.ensure_mutable_tail(el, tail_size); + std::uninitialized_copy(r.tail->leaf(), + r.tail->leaf() + remaining, + l.tail->leaf() + tail_size); + try { + auto new_tail = node_t::copy_leaf_e(el, r.tail, remaining, r.size); + try { + l.push_tail_mut(el, tail_offst, l.tail, branches); + l.tail = new_tail; + l.size += r.size; + return; + } catch (...) { + node_t::delete_leaf(new_tail, r.size - remaining); + throw; + } + } catch (...) { + destroy_n(r.tail->leaf() + tail_size, remaining); + throw; + } + } + } else if (l.tail_offset() == 0) { + if (supports_transient_concat) { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees_mut( + el, + el, l.tail, tail_size, + MemoryPolicy::transience_t::noone, + r.root, r.shift, r.tail_offset()); + assert(concated.shift() == concated.node()->compute_shift()); + assert(concated.node()->check(concated.shift(), l.size + r.tail_offset())); + l.size += r.size; + l.shift = concated.shift(); + l.root = concated.node(); + l.tail = r.tail; + } else { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees(l.tail, tail_size, + r.root, r.shift, r.tail_offset()); + l = { l.size + r.size, concated.shift(), + concated.node(), r.tail->inc() }; + return; + } + } else { + if (supports_transient_concat) { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees_mut( + el, + el, l.root, l.shift, tail_offst, l.tail, tail_size, + MemoryPolicy::transience_t::noone, + r.root, r.shift, r.tail_offset()); + assert(concated.shift() == concated.node()->compute_shift()); + assert(concated.node()->check(concated.shift(), l.size + r.tail_offset())); + l.size += r.size; + l.shift = concated.shift(); + l.root = concated.node(); + l.tail = r.tail; + } else { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees(l.root, l.shift, tail_offst, + l.tail, tail_size, + r.root, r.shift, r.tail_offset()); + l = { l.size + r.size, concated.shift(), + concated.node(), r.tail->inc() }; + } + } + } + + friend void concat_mut_r(const rrbtree& l, rrbtree& r, edit_t er) + { + assert(&l != &r); + assert(r.size < (std::numeric_limits::max() - l.size)); + using std::get; + if (r.size == 0) + r = std::move(l); + else if (l.size == 0) + return; + else if (r.tail_offset() == 0) { + // just concat the tail, similar to push_back + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + if (tail_size == branches) { + // this could be improved by making sure that the + // newly created nodes as part of the `push_tail()` + // are tagged with `er` + auto res = l.push_tail(l.root, l.shift, tail_offst, + l.tail, tail_size); + l.tail->inc(); // note: leak if mutably concatenated + // with itself, but this is forbidden + // by the interface + r = { l.size + r.size, get<0>(res), get<1>(res), + r.tail->inc() }; + return; + } else if (tail_size + r.size <= branches) { + // doing this in a exception way mutating way is very + // tricky while potential performance gains are + // minimal (we need to move every element of the right + // tail anyways to make space for the left tail) + // + // we could however improve this by at least moving the + // elements of the right tail... + auto new_tail = node_t::copy_leaf(l.tail, tail_size, + r.tail, r.size); + r = { l.size + r.size, l.shift, l.root->inc(), new_tail }; + return; + } else { + // like the immutable version + auto remaining = branches - tail_size; + auto add_tail = node_t::copy_leaf_e(er, + l.tail, tail_size, + r.tail, remaining); + try { + auto new_tail = node_t::copy_leaf_e(er, r.tail, remaining, r.size); + try { + // this could be improved by making sure that the + // newly created nodes as part of the `push_tail()` + // are tagged with `er` + auto new_root = l.push_tail(l.root, l.shift, tail_offst, + add_tail, branches); + r = { l.size + r.size, + get<0>(new_root), get<1>(new_root), + new_tail }; + return; + } catch (...) { + node_t::delete_leaf(new_tail, r.size - remaining); + throw; + } + } catch (...) { + node_t::delete_leaf(add_tail, branches); + throw; + } + return; + } + } else if (l.tail_offset() == 0) { + if (supports_transient_concat) { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees_mut( + er, + MemoryPolicy::transience_t::noone, l.tail, tail_size, + er,r.root, r.shift, r.tail_offset()); + assert(concated.shift() == concated.node()->compute_shift()); + assert(concated.node()->check(concated.shift(), l.size + r.tail_offset())); + r.size += l.size; + r.shift = concated.shift(); + r.root = concated.node(); + } else { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees(l.tail, tail_size, + r.root, r.shift, r.tail_offset()); + r = { l.size + r.size, concated.shift(), + concated.node(), r.tail->inc() }; + return; + } + } else { + if (supports_transient_concat) { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees_mut( + er, + MemoryPolicy::transience_t::noone, + l.root, l.shift, tail_offst, l.tail, tail_size, + er, r.root, r.shift, r.tail_offset()); + assert(concated.shift() == concated.node()->compute_shift()); + assert(concated.node()->check(concated.shift(), l.size + r.tail_offset())); + r.size += l.size; + r.shift = concated.shift(); + r.root = concated.node(); + return; + } else { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees(l.root, l.shift, tail_offst, + l.tail, tail_size, + r.root, r.shift, r.tail_offset()); + r = { l.size + r.size, concated.shift(), + concated.node(), r.tail->inc() }; + return; + } + } + } + + friend void concat_mut_lr_l(rrbtree& l, edit_t el, rrbtree& r, edit_t er) + { + assert(&l != &r); + assert(r.size < (std::numeric_limits::max() - l.size)); + using std::get; + if (l.size == 0) + l = r; + else if (r.size == 0) + return; + else if (r.tail_offset() == 0) { + // just concat the tail, similar to push_back + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + if (tail_size == branches) { + l.push_tail_mut(el, tail_offst, l.tail, tail_size); + l.tail = r.tail->inc(); + l.size += r.size; + return; + } else if (tail_size + r.size <= branches) { + l.ensure_mutable_tail(el, tail_size); + if (r.tail->can_mutate(er)) + uninitialized_move(r.tail->leaf(), + r.tail->leaf() + r.size, + l.tail->leaf() + tail_size); + else + std::uninitialized_copy(r.tail->leaf(), + r.tail->leaf() + r.size, + l.tail->leaf() + tail_size); + l.size += r.size; + return; + } else { + auto remaining = branches - tail_size; + l.ensure_mutable_tail(el, tail_size); + if (r.tail->can_mutate(er)) + uninitialized_move(r.tail->leaf(), + r.tail->leaf() + remaining, + l.tail->leaf() + tail_size); + else + std::uninitialized_copy(r.tail->leaf(), + r.tail->leaf() + remaining, + l.tail->leaf() + tail_size); + try { + auto new_tail = node_t::copy_leaf_e(el, r.tail, remaining, r.size); + try { + l.push_tail_mut(el, tail_offst, l.tail, branches); + l.tail = new_tail; + l.size += r.size; + return; + } catch (...) { + node_t::delete_leaf(new_tail, r.size - remaining); + throw; + } + } catch (...) { + destroy_n(r.tail->leaf() + tail_size, remaining); + throw; + } + } + } else if (l.tail_offset() == 0) { + if (supports_transient_concat) { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees_mut( + el, + el, l.tail, tail_size, + er, r.root, r.shift, r.tail_offset()); + assert(concated.shift() == concated.node()->compute_shift()); + assert(concated.node()->check(concated.shift(), l.size + r.tail_offset())); + l.size += r.size; + l.shift = concated.shift(); + l.root = concated.node(); + l.tail = r.tail; + r.hard_reset(); + } else { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees(l.tail, tail_size, + r.root, r.shift, r.tail_offset()); + l = { l.size + r.size, concated.shift(), + concated.node(), r.tail->inc() }; + return; + } + } else { + if (supports_transient_concat) { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees_mut( + el, + el, l.root, l.shift, tail_offst, l.tail, tail_size, + er, r.root, r.shift, r.tail_offset()); + assert(concated.shift() == concated.node()->compute_shift()); + assert(concated.node()->check(concated.shift(), l.size + r.tail_offset())); + l.size += r.size; + l.shift = concated.shift(); + l.root = concated.node(); + l.tail = r.tail; + r.hard_reset(); + } else { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees(l.root, l.shift, tail_offst, + l.tail, tail_size, + r.root, r.shift, r.tail_offset()); + l = { l.size + r.size, concated.shift(), + concated.node(), r.tail->inc() }; + } + } + } + + friend void concat_mut_lr_r(rrbtree& l, edit_t el, rrbtree& r, edit_t er) + { + assert(&l != &r); + assert(r.size < (std::numeric_limits::max() - l.size)); + using std::get; + if (r.size == 0) + r = l; + else if (l.size == 0) + return; + else if (r.tail_offset() == 0) { + // just concat the tail, similar to push_back + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + if (tail_size == branches) { + // this could be improved by making sure that the + // newly created nodes as part of the `push_tail()` + // are tagged with `er` + auto res = l.push_tail(l.root, l.shift, tail_offst, + l.tail, tail_size); + r = { l.size + r.size, get<0>(res), get<1>(res), + r.tail->inc() }; + return; + } else if (tail_size + r.size <= branches) { + // doing this in a exception way mutating way is very + // tricky while potential performance gains are + // minimal (we need to move every element of the right + // tail anyways to make space for the left tail) + // + // we could however improve this by at least moving the + // elements of the mutable tails... + auto new_tail = node_t::copy_leaf(l.tail, tail_size, + r.tail, r.size); + r = { l.size + r.size, l.shift, l.root->inc(), new_tail }; + return; + } else { + // like the immutable version. + // we could improve this also by moving elements + // instead of just copying them + auto remaining = branches - tail_size; + auto add_tail = node_t::copy_leaf_e(er, + l.tail, tail_size, + r.tail, remaining); + try { + auto new_tail = node_t::copy_leaf_e(er, r.tail, remaining, r.size); + try { + // this could be improved by making sure that the + // newly created nodes as part of the `push_tail()` + // are tagged with `er` + auto new_root = l.push_tail(l.root, l.shift, tail_offst, + add_tail, branches); + r = { l.size + r.size, + get<0>(new_root), get<1>(new_root), + new_tail }; + return; + } catch (...) { + node_t::delete_leaf(new_tail, r.size - remaining); + throw; + } + } catch (...) { + node_t::delete_leaf(add_tail, branches); + throw; + } + return; + } + } else if (l.tail_offset() == 0) { + if (supports_transient_concat) { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees_mut( + er, + el, l.tail, tail_size, + er,r.root, r.shift, r.tail_offset()); + assert(concated.shift() == concated.node()->compute_shift()); + assert(concated.node()->check(concated.shift(), l.size + r.tail_offset())); + r.size += l.size; + r.shift = concated.shift(); + r.root = concated.node(); + l.hard_reset(); + } else { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees(l.tail, tail_size, + r.root, r.shift, r.tail_offset()); + r = { l.size + r.size, concated.shift(), + concated.node(), r.tail->inc() }; + return; + } + } else { + if (supports_transient_concat) { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees_mut( + er, + el, l.root, l.shift, tail_offst, l.tail, tail_size, + er, r.root, r.shift, r.tail_offset()); + assert(concated.shift() == concated.node()->compute_shift()); + assert(concated.node()->check(concated.shift(), l.size + r.tail_offset())); + r.size += l.size; + r.shift = concated.shift(); + r.root = concated.node(); + l.hard_reset(); + } else { + auto tail_offst = l.tail_offset(); + auto tail_size = l.size - tail_offst; + auto concated = concat_trees(l.root, l.shift, tail_offst, + l.tail, tail_size, + r.root, r.shift, r.tail_offset()); + r = { l.size + r.size, concated.shift(), + concated.node(), r.tail->inc() }; + } + } + } + + void hard_reset() + { + assert(supports_transient_concat); + size = empty.size; + shift = empty.shift; + root = empty.root; + tail = empty.tail; + } + + bool check_tree() const + { + assert(shift <= sizeof(size_t) * 8 - BL); + assert(shift >= BL); + assert(tail_offset() <= size); + assert(tail_size() <= branches); +#if IMMER_DEBUG_DEEP_CHECK + assert(check_root()); + assert(check_tail()); +#endif + return true; + } + + bool check_tail() const + { +#if IMMER_DEBUG_DEEP_CHECK + if (tail_size() > 0) + assert(tail->check(endshift, tail_size())); +#endif + return true; + } + + bool check_root() const + { +#if IMMER_DEBUG_DEEP_CHECK + if (tail_offset() > 0) + assert(root->check(shift, tail_offset())); + else { + assert(root->kind() == node_t::kind_t::inner); + assert(shift == BL); + } +#endif + return true; + } + +#if IMMER_DEBUG_PRINT + void debug_print() const + { + std::cerr + << "--" << std::endl + << "{" << std::endl + << " size = " << size << std::endl + << " shift = " << shift << std::endl + << " root = " << std::endl; + debug_print_node(root, shift, tail_offset()); + std::cerr << " tail = " << std::endl; + debug_print_node(tail, endshift, tail_size()); + std::cerr << "}" << std::endl; + } + + void debug_print_indent(unsigned indent) const + { + while (indent --> 0) + std::cerr << ' '; + } + + void debug_print_node(node_t* node, + shift_t shift, + size_t size, + unsigned indent = 8) const + { + const auto indent_step = 4; + + if (shift == endshift) { + debug_print_indent(indent); + std::cerr << "- {" << size << "} " + << pretty_print_array(node->leaf(), size) + << std::endl; + } else if (auto r = node->relaxed()) { + auto count = r->d.count; + debug_print_indent(indent); + std::cerr << "# {" << size << "} " + << pretty_print_array(r->d.sizes, r->d.count) + << std::endl; + auto last_size = size_t{}; + for (auto i = 0; i < count; ++i) { + debug_print_node(node->inner()[i], + shift - B, + r->d.sizes[i] - last_size, + indent + indent_step); + last_size = r->d.sizes[i]; + } + } else { + debug_print_indent(indent); + std::cerr << "+ {" << size << "}" << std::endl; + auto count = (size >> shift) + + (size - ((size >> shift) << shift) > 0); + if (count) { + for (auto i = 0; i < count - 1; ++i) + debug_print_node(node->inner()[i], + shift - B, + 1 << shift, + indent + indent_step); + debug_print_node(node->inner()[count - 1], + shift - B, + size - ((count - 1) << shift), + indent + indent_step); + } + } + } +#endif // IMMER_DEBUG_PRINT +}; + +template +const rrbtree rrbtree::empty = { + 0, + BL, + node_t::make_inner_n(0u), + node_t::make_leaf_n(0u) +}; + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/rrbtree_iterator.hpp b/src/immer/detail/rbts/rrbtree_iterator.hpp new file mode 100644 index 000000000000..36222f976839 --- /dev/null +++ b/src/immer/detail/rbts/rrbtree_iterator.hpp @@ -0,0 +1,100 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { +namespace detail { +namespace rbts { + +template +struct rrbtree_iterator + : iterator_facade, + std::random_access_iterator_tag, + T, + const T&> +{ + using tree_t = rrbtree; + using region_t = std::tuple; + + struct end_t {}; + + const tree_t& impl() const { return *v_; } + size_t index() const { return i_; } + + rrbtree_iterator() = default; + + rrbtree_iterator(const tree_t& v) + : v_ { &v } + , i_ { 0 } + , curr_ { nullptr, ~size_t{}, ~size_t{} } + { + } + + rrbtree_iterator(const tree_t& v, end_t) + : v_ { &v } + , i_ { v.size } + , curr_ { nullptr, ~size_t{}, ~size_t{} } + {} + +private: + friend iterator_core_access; + + const tree_t* v_; + size_t i_; + mutable region_t curr_; + + void increment() + { + using std::get; + assert(i_ < v_->size); + ++i_; + } + + void decrement() + { + using std::get; + assert(i_ > 0); + --i_; + } + + void advance(std::ptrdiff_t n) + { + using std::get; + assert(n <= 0 || i_ + static_cast(n) <= v_->size); + assert(n >= 0 || static_cast(-n) <= i_); + i_ += n; + } + + bool equal(const rrbtree_iterator& other) const + { + return i_ == other.i_; + } + + std::ptrdiff_t distance_to(const rrbtree_iterator& other) const + { + return other.i_ > i_ + ? static_cast(other.i_ - i_) + : - static_cast(i_ - other.i_); + } + + const T& dereference() const + { + using std::get; + if (i_ < get<1>(curr_) || i_ >= get<2>(curr_)) + curr_ = v_->region_for(i_); + return get<0>(curr_)[i_ - get<1>(curr_)]; + } +}; + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/rbts/visitor.hpp b/src/immer/detail/rbts/visitor.hpp new file mode 100644 index 000000000000..d85f96d865ed --- /dev/null +++ b/src/immer/detail/rbts/visitor.hpp @@ -0,0 +1,83 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { +namespace detail { +namespace rbts { + +struct visitor_tag {}; + +template +using fn_visitor = std::tuple; + +template +auto make_visitor(Fns&& ...fns) +{ + return std::make_tuple(visitor_tag{}, std::forward(fns)...); +} + +template +decltype(auto) visit_relaxed(fn_visitor v, Args&& ...args) +{ return std::get<1>(v)(v, std::forward(args)...); } + +template +decltype(auto) visit_regular(fn_visitor v, Args&& ...args) +{ return std::get<2>(v)(v, std::forward(args)...); } + +template +decltype(auto) visit_leaf(fn_visitor v, Args&& ...args) +{ return std::get<3>(v)(v, std::forward(args)...); } + +template +decltype(auto) visit_inner(fn_visitor v, Args&& ...args) +{ return std::get<1>(v)(v, std::forward(args)...); } + +template +decltype(auto) visit_leaf(fn_visitor v, Args&& ...args) +{ return std::get<2>(v)(v, std::forward(args)...); } + +template +decltype(auto) visit_node(fn_visitor v, Args&& ...args) +{ return std::get<1>(v)(v, std::forward(args)...); } + +template +decltype(auto) visit_relaxed(Visitor&& v, Args&& ...args) +{ + return visit_inner(std::forward(v), + std::forward(args)...); +} + +template +decltype(auto) visit_regular(Visitor&& v, Args&& ...args) +{ + return visit_inner(std::forward(v), + std::forward(args)...); +} + +template +decltype(auto) visit_inner(Visitor&& v, Args&& ...args) +{ + return visit_node(std::forward(v), + std::forward(args)...); +} + +template +decltype(auto) visit_leaf(Visitor&& v, Args&& ...args) +{ + return visit_node(std::forward(v), + std::forward(args)...); +} + +} // namespace rbts +} // namespace detail +} // namespace immer diff --git a/src/immer/detail/ref_count_base.hpp b/src/immer/detail/ref_count_base.hpp new file mode 100644 index 000000000000..a20428b13f70 --- /dev/null +++ b/src/immer/detail/ref_count_base.hpp @@ -0,0 +1,36 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { +namespace detail { + +template +struct ref_count_base +{ + mutable std::atomic ref_count { 0 }; + + friend void intrusive_ptr_add_ref(const Deriv* x) + { + x->ref_count.fetch_add(1, std::memory_order_relaxed); + } + + friend void intrusive_ptr_release(const Deriv* x) + { + if (x->ref_count.fetch_sub(1, std::memory_order_release) == 1) { + std::atomic_thread_fence(std::memory_order_acquire); + delete x; + } + } +}; + +} /* namespace detail */ +} /* namespace immer */ diff --git a/src/immer/detail/util.hpp b/src/immer/detail/util.hpp new file mode 100644 index 000000000000..7490357dbf60 --- /dev/null +++ b/src/immer/detail/util.hpp @@ -0,0 +1,123 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +#include +#include +#include +#include + +namespace immer { +namespace detail { + +template +using aligned_storage_for = + typename std::aligned_storage::type; + +template +T& auto_const_cast(const T& x) { return const_cast(x); } +template +T&& auto_const_cast(const T&& x) { return const_cast(std::move(x)); } + +template +auto uninitialized_move(Iter1 in1, Iter1 in2, Iter2 out) +{ + return std::uninitialized_copy(std::make_move_iterator(in1), + std::make_move_iterator(in2), + out); +} + +template +void destroy(T* first, T* last) +{ + for (; first != last; ++first) + first->~T(); +} + +template +void destroy_n(T* p, Size n) +{ + auto e = p + n; + for (; p != e; ++p) + p->~T(); +} + +template +T* make(Args&& ...args) +{ + auto ptr = Heap::allocate(sizeof(T)); + try { + return new (ptr) T{std::forward(args)...}; + } catch (...) { + Heap::deallocate(sizeof(T), ptr); + throw; + } +} + +struct not_supported_t {}; +struct empty_t {}; + +template +struct exact_t +{ + T value; + exact_t(T v) : value{v} {}; +}; + +template +inline constexpr auto clz_(T) -> not_supported_t { IMMER_UNREACHABLE; return {}; } +inline constexpr auto clz_(unsigned int x) { return __builtin_clz(x); } +inline constexpr auto clz_(unsigned long x) { return __builtin_clzl(x); } +inline constexpr auto clz_(unsigned long long x) { return __builtin_clzll(x); } + +template +inline constexpr T log2_aux(T x, T r = 0) +{ + return x <= 1 ? r : log2(x >> 1, r + 1); +} + +template +inline constexpr auto log2(T x) + -> std::enable_if_t{}, T> +{ + return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - clz_(x); +} + +template +inline constexpr auto log2(T x) + -> std::enable_if_t{}, T> +{ + return log2_aux(x); +} + +template +auto static_if(F&& f) -> std::enable_if_t +{ std::forward(f)(empty_t{}); } +template +auto static_if(F&& f) -> std::enable_if_t +{} + +template +auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t +{ return std::forward(f1)(empty_t{}); } +template +auto static_if(F1&& f1, F2&& f2) -> std::enable_if_t +{ return std::forward(f2)(empty_t{}); } + +template +struct constantly +{ + template + T operator() (Args&&...) const { return value; } +}; + +} // namespace detail +} // namespace immer diff --git a/src/immer/experimental/detail/dvektor_impl.hpp b/src/immer/experimental/detail/dvektor_impl.hpp new file mode 100644 index 000000000000..df273aab71c9 --- /dev/null +++ b/src/immer/experimental/detail/dvektor_impl.hpp @@ -0,0 +1,512 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace immer { +namespace detail { +namespace dvektor { + +constexpr auto fast_log2(std::size_t x) +{ + return x == 0 ? 0 : sizeof(std::size_t) * 8 - 1 - __builtin_clzl(x); +} + +template +constexpr T branches = T{1} << B; + +template +constexpr T mask = branches - 1; + +template +constexpr auto max_depth = + fast_log2(std::numeric_limits::max()) / B; + +template +struct node; + +template +using node_ptr = boost::intrusive_ptr >; + +template +using leaf_node = std::array; + +template +using inner_node = std::array, 1 << B>; + +template +struct node : enable_intrusive_ptr, typename MP::refcount> + , enable_optimized_heap_policy, typename MP::heap> +{ + using leaf_node_t = leaf_node; + using inner_node_t = inner_node; + + enum + { + leaf_kind, + inner_kind + } kind; + + union data_t + { + leaf_node_t leaf; + inner_node_t inner; + data_t(leaf_node_t n) : leaf(std::move(n)) {} + data_t(inner_node_t n) : inner(std::move(n)) {} + ~data_t() {} + } data; + + ~node() + { + switch (kind) { + case leaf_kind: + data.leaf.~leaf_node_t(); + break; + case inner_kind: + data.inner.~inner_node_t(); + break; + } + } + + node(leaf_node n) + : kind{leaf_kind} + , data{std::move(n)} + {} + + node(inner_node n) + : kind{inner_kind} + , data{std::move(n)} + {} + + inner_node_t& inner() & { + assert(kind == inner_kind); + return data.inner; + } + const inner_node_t& inner() const& { + assert(kind == inner_kind); + return data.inner; + } + inner_node_t&& inner() && { + assert(kind == inner_kind); + return std::move(data.inner); + } + + leaf_node_t& leaf() & { + assert(kind == leaf_kind); + return data.leaf; + } + const leaf_node_t& leaf() const& { + assert(kind == leaf_kind); + return data.leaf; + } + leaf_node_t&& leaf() && { + assert(kind == leaf_kind); + return std::move(data.leaf); + } +}; + +template +auto make_node(Ts&& ...xs) + -> boost::intrusive_ptr> +{ + return new node(std::forward(xs)...); +} + +template +struct ref +{ + using inner_t = inner_node; + using leaf_t = leaf_node; + using node_t = node; + using node_ptr_t = node_ptr; + + unsigned depth; + std::array> display; + + template + static auto make_node(Ts&& ...xs) + { + return dvektor::make_node(std::forward(xs)...); + } + + const T& get_elem(std::size_t index, std::size_t xr) const + { + auto display_idx = fast_log2(xr) / B; + auto node = display[display_idx].get(); + auto shift = display_idx * B; + while (display_idx--) { + node = node->inner() [(index >> shift) & mask].get(); + shift -= B; + } + return node->leaf() [index & mask]; + } + + node_ptr_t null_slot_and_copy_inner(node_ptr_t& node, std::size_t idx) + { + auto& n = node->inner(); + auto x = node_ptr_t{}; + x.swap(n[idx]); + return copy_of_inner(x); + } + + node_ptr_t null_slot_and_copy_leaf(node_ptr_t& node, std::size_t idx) + { + auto& n = node->inner(); + auto x = node_ptr_t{}; + x.swap(n[idx]); + return copy_of_leaf(x); + } + + node_ptr_t copy_of_inner(const node_ptr_t& n) + { + return make_node(n->inner()); + } + + node_ptr_t copy_of_leaf(const node_ptr_t& n) + { + return make_node(n->leaf()); + } + + void stabilize(std::size_t index) + { + auto shift = B; + for (auto i = 1u; i < depth; ++i) + { + display[i] = copy_of_inner(display[i]); + display[i]->inner() [(index >> shift) & mask] + = display[i - 1]; + shift += B; + } + } + + void goto_pos_writable_from_clean(std::size_t old_index, + std::size_t index, + std::size_t xr) + { + assert(depth); + auto d = depth - 1; + if (d == 0) { + display[0] = copy_of_leaf(display[0]); + } else { + IMMER_UNREACHABLE; + display[d] = copy_of_inner(display[d]); + auto shift = B * d; + while (--d) { + display[d] = null_slot_and_copy_inner( + display[d + 1], + (index >> shift) & mask); + shift -= B; + } + display[0] = null_slot_and_copy_leaf( + display[1], + (index >> B) & mask); + } + } + + void goto_pos_writable_from_dirty(std::size_t old_index, + std::size_t new_index, + std::size_t xr) + { + assert(depth); + if (xr < (1 << B)) { + display[0] = copy_of_leaf(display[0]); + } else { + auto display_idx = fast_log2(xr) / B; + auto shift = B; + for (auto i = 1u; i <= display_idx; ++i) { + display[i] = copy_of_inner(display[i]); + display[i]->inner() [(old_index >> shift) & mask] + = display[i - 1]; + shift += B; + } + for (auto i = display_idx - 1; i > 0; --i) { + shift -= B; + display[i] = null_slot_and_copy_inner( + display[i + 1], + (new_index >> shift) & mask); + } + display[0] = null_slot_and_copy_leaf( + display[1], + (new_index >> B) & mask); + } + } + + void goto_fresh_pos_writable_from_clean(std::size_t old_index, + std::size_t new_index, + std::size_t xr) + { + auto display_idx = fast_log2(xr) / B; + if (display_idx > 0) { + auto shift = display_idx * B; + if (display_idx == depth) { + display[display_idx] = make_node(inner_t{}); + display[display_idx]->inner() + [(old_index >> shift) & mask] = + display[display_idx - 1]; + ++depth; + } + while (--display_idx) { + auto node = display[display_idx + 1]->inner() + [(new_index >> shift) & mask]; + display[display_idx] = node + ? std::move(node) + : make_node(inner_t{}); + + } + display[0] = make_node(leaf_t{}); + } + } + + void goto_fresh_pos_writable_from_dirty(std::size_t old_index, + std::size_t new_index, + std::size_t xr) + { + stabilize(old_index); + goto_fresh_pos_writable_from_clean(old_index, new_index, xr); + } + + void goto_next_block_start(std::size_t index, std::size_t xr) + { + auto display_idx = fast_log2(xr) / B; + auto shift = display_idx * B; + if (display_idx > 0) { + display[display_idx - 1] = display[display_idx]->inner() + [(index >> shift) & mask]; + while (--display_idx) + display[display_idx - 1] = display[display_idx]->inner()[0]; + } + } + + void goto_pos(std::size_t index, std::size_t xr) + { + auto display_idx = fast_log2(xr) / B; + auto shift = display_idx * B; + if (display_idx) { + do { + display[display_idx - 1] = display[display_idx]->inner() + [(index >> shift) & mask]; + shift -= B; + } while (--display_idx); + } + } +}; + +template +struct impl +{ + using inner_t = inner_node; + using leaf_t = leaf_node; + using node_t = node; + using node_ptr_t = node_ptr; + using ref_t = ref; + + std::size_t size; + std::size_t focus; + bool dirty; + ref_t p; + + template + static auto make_node(Ts&& ...xs) + { + return dvektor::make_node(std::forward(xs)...); + } + + void goto_pos_writable(std::size_t old_index, + std::size_t new_index, + std::size_t xr) + { + if (dirty) { + p.goto_pos_writable_from_dirty(old_index, new_index, xr); + } else { + p.goto_pos_writable_from_clean(old_index, new_index, xr); + dirty = true; + } + } + + void goto_fresh_pos_writable(std::size_t old_index, + std::size_t new_index, + std::size_t xr) + { + if (dirty) { + p.goto_fresh_pos_writable_from_dirty(old_index, new_index, xr); + } else { + p.goto_fresh_pos_writable_from_clean(old_index, new_index, xr); + dirty = true; + } + } + + impl push_back(T value) const + { + if (size) { + auto block_index = size & ~mask; + auto lo = size & mask; + if (size != block_index) { + auto s = impl{ size + 1, block_index, dirty, p }; + s.goto_pos_writable(focus, block_index, focus ^ block_index); + s.p.display[0]->leaf() [lo] = std::move(value); + return s; + } else { + auto s = impl{ size + 1, block_index, dirty, p }; + s.goto_fresh_pos_writable(focus, block_index, focus ^ block_index); + s.p.display[0]->leaf() [lo] = std::move(value); + return s; + } + } else { + return impl{ + 1, 0, false, + { 1, {{ make_node(leaf_t{{std::move(value)}}) }} } + }; + } + } + + const T& get(std::size_t index) const + { + return p.get_elem(index, index ^ focus); + } + + template + impl update(std::size_t idx, FnT&& fn) const + { + auto s = impl{ size, idx, dirty, p }; + s.goto_pos_writable(focus, idx, focus ^ idx); + auto& v = s.p.display[0]->leaf() [idx & mask]; + v = fn(std::move(v)); + return s; + } + + impl assoc(std::size_t idx, T value) const + { + return update(idx, [&] (auto&&) { + return std::move(value); + }); + } +}; + +template +const impl empty = { + 0, + 0, + false, + ref {1, {}} +}; + +template +struct iterator : boost::iterator_facade< + iterator, + T, + boost::random_access_traversal_tag, + const T&> +{ + struct end_t {}; + + iterator() = default; + + iterator(const impl& v) + : p_{ v.p } + , i_{ 0 } + , base_{ 0 } + { + if (v.dirty) + p_.stabilize(v.focus); + p_.goto_pos(0, 0 ^ v.focus); + curr_ = p_.display[0]->leaf().begin(); + } + + iterator(const impl& v, end_t) + : p_{ v.p } + , i_{ v.size } + , base_{ (v.size-1) & ~mask } + { + if (v.dirty) + p_.stabilize(v.focus); + p_.goto_pos(base_, base_ ^ v.focus); + curr_ = p_.display[0]->leaf().begin() + (i_ - base_); + } + +private: + friend class boost::iterator_core_access; + using leaf_iterator = typename leaf_node::const_iterator; + + ref p_; + std::size_t i_; + std::size_t base_; + leaf_iterator curr_; + + void increment() + { + ++i_; + if (i_ - base_ < branches) { + ++curr_; + } else { + auto new_base = base_ + branches; + p_.goto_next_block_start(new_base, base_ ^ new_base); + base_ = new_base; + curr_ = p_.display[0]->leaf().begin(); + } + } + + void decrement() + { + assert(i_ > 0); + --i_; + if (i_ >= base_) { + --curr_; + } else { + auto new_base = base_ - branches; + p_.goto_pos(new_base, base_ ^ new_base); + base_ = new_base; + curr_ = std::prev(p_.display[0]->leaf().end()); + } + } + + void advance(std::ptrdiff_t n) + { + i_ += n; + if (i_ <= base_ && i_ - base_ < branches) { + curr_ += n; + } else { + auto new_base = i_ & ~mask; + p_.goto_pos(new_base, base_ ^ new_base); + base_ = new_base; + curr_ = p_.display[0]->leaf().begin() + (i_ - base_); + } + } + + bool equal(const iterator& other) const + { + return i_ == other.i_; + } + + std::ptrdiff_t distance_to(const iterator& other) const + { + return other.i_ > i_ + ? static_cast(other.i_ - i_) + : - static_cast(i_ - other.i_); + } + + const T& dereference() const + { + return *curr_; + } +}; + +} /* namespace dvektor */ +} /* namespace detail */ +} /* namespace immer */ diff --git a/src/immer/experimental/dvektor.hpp b/src/immer/experimental/dvektor.hpp new file mode 100644 index 000000000000..7aa0bde7b3da --- /dev/null +++ b/src/immer/experimental/dvektor.hpp @@ -0,0 +1,63 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { + +template +class dvektor +{ + using impl_t = detail::dvektor::impl; + +public: + using value_type = T; + using reference = const T&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using const_reference = const T&; + + using iterator = detail::dvektor::iterator; + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + + dvektor() = default; + + iterator begin() const { return {impl_}; } + iterator end() const { return {impl_, typename iterator::end_t{}}; } + + reverse_iterator rbegin() const { return reverse_iterator{end()}; } + reverse_iterator rend() const { return reverse_iterator{begin()}; } + + std::size_t size() const { return impl_.size; } + bool empty() const { return impl_.size == 0; } + + reference operator[] (size_type index) const + { return impl_.get(index); } + + dvektor push_back(value_type value) const + { return { impl_.push_back(std::move(value)) }; } + + dvektor assoc(std::size_t idx, value_type value) const + { return { impl_.assoc(idx, std::move(value)) }; } + + template + dvektor update(std::size_t idx, FnT&& fn) const + { return { impl_.update(idx, std::forward(fn)) }; } + +private: + dvektor(impl_t impl) : impl_(std::move(impl)) {} + impl_t impl_ = detail::dvektor::empty; +}; + +} // namespace immer diff --git a/src/immer/flex_vector.hpp b/src/immer/flex_vector.hpp new file mode 100644 index 000000000000..77313267b13f --- /dev/null +++ b/src/immer/flex_vector.hpp @@ -0,0 +1,500 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +namespace immer { + +template +class vector; + +template +class flex_vector_transient; + +/*! + * Immutable sequential container supporting both random access, + * structural sharing and efficient concatenation and slicing. + * + * @tparam T The type of the values to be stored in the container. + * @tparam MemoryPolicy Memory management policy. See @ref + * memory_policy. + * + * @rst + * + * This container is very similar to `vector`_ but also supports + * :math:`O(log(size))` *concatenation*, *slicing* and *insertion* at + * any point. Its performance characteristics are almost identical + * until one of these operations is performed. After that, + * performance is degraded by a constant factor that usually oscilates + * in the range :math:`[1, 2)` depending on the operation and the + * amount of flexible operations that have been performed. + * + * .. tip:: A `vector`_ can be converted to a `flex_vector`_ in + * constant time without any allocation. This is so because the + * internal structure of a *vector* is a strict subset of the + * internal structure of a *flexible vector*. You can take + * advantage of this property by creating normal vectors as long as + * the flexible operations are not needed, and convert later in + * your processing pipeline once and if these are needed. + * + * @endrst + */ +template > +class flex_vector +{ + using impl_t = detail::rbts::rrbtree; + + using move_t = + std::integral_constant; + +public: + static constexpr auto bits = B; + static constexpr auto bits_leaf = BL; + using memory_policy = MemoryPolicy; + + using value_type = T; + using reference = const T&; + using size_type = detail::rbts::size_t; + using difference_type = std::ptrdiff_t; + using const_reference = const T&; + + using iterator = detail::rbts::rrbtree_iterator; + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + + using transient_type = flex_vector_transient; + + /*! + * Default constructor. It creates a flex_vector of `size() == 0`. + * It does not allocate memory and its complexity is @f$ O(1) @f$. + */ + flex_vector() = default; + + /*! + * Constructs a vector containing the elements in `values`. + */ + flex_vector(std::initializer_list values) + : impl_{impl_t::from_initializer_list(values)} + {} + + /*! + * Constructs a vector containing the elements in the range + * defined by the input iterators `first` and `last`. + */ + template + flex_vector(Iter first, Iter last) + : impl_{impl_t::from_range(first, last)} + {} + + /*! + * Constructs a vector containing the element `val` repeated `n` + * times. + */ + flex_vector(size_type n, T v = {}) + : impl_{impl_t::from_fill(n, v)} + {} + + /*! + * Default constructor. It creates a flex_vector with the same + * contents as `v`. It does not allocate memory and is + * @f$ O(1) @f$. + */ + flex_vector(vector v) + : impl_ { v.impl_.size, v.impl_.shift, + v.impl_.root->inc(), v.impl_.tail->inc() } + {} + + /*! + * Returns an iterator pointing at the first element of the + * collection. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + iterator begin() const { return {impl_}; } + + /*! + * Returns an iterator pointing just after the last element of the + * collection. It does not allocate and its complexity is @f$ O(1) @f$. + */ + iterator end() const { return {impl_, typename iterator::end_t{}}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing at the first element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing after the last element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rend() const { return reverse_iterator{begin()}; } + + /*! + * Returns the number of elements in the container. It does + * not allocate memory and its complexity is @f$ O(1) @f$. + */ + size_type size() const { return impl_.size; } + + /*! + * Returns `true` if there are no elements in the container. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + bool empty() const { return impl_.size == 0; } + + /*! + * Access the last element. + */ + const T& back() const { return impl_.back(); } + + /*! + * Access the first element. + */ + const T& front() const { return impl_.front(); } + + /*! + * Returns a `const` reference to the element at position `index`. + * It is undefined when @f$ 0 index \geq size() @f$. It does not + * allocate memory and its complexity is *effectively* @f$ O(1) + * @f$. + */ + reference operator[] (size_type index) const + { return impl_.get(index); } + + /*! + * Returns a `const` reference to the element at position + * `index`. It throws an `std::out_of_range` exception when @f$ + * index \geq size() @f$. It does not allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + reference at(size_type index) const + { return impl_.get_check(index); } + + /*! + * Returns whether the vectors are equal. + */ + bool operator==(const flex_vector& other) const + { return impl_.equals(other.impl_); } + bool operator!=(const flex_vector& other) const + { return !(*this == other); } + + /*! + * Returns a flex_vector with `value` inserted at the end. It may + * allocate memory and its complexity is *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: push-back/start + * :end-before: push-back/end + * + * @endrst + */ + flex_vector push_back(value_type value) const& + { return impl_.push_back(std::move(value)); } + + decltype(auto) push_back(value_type value) && + { return push_back_move(move_t{}, std::move(value)); } + + /*! + * Returns a flex_vector with `value` inserted at the frony. It may + * allocate memory and its complexity is @f$ O(log(size)) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: push-front/start + * :end-before: push-front/end + * + * @endrst + */ + flex_vector push_front(value_type value) const + { return flex_vector{}.push_back(value) + *this; } + + /*! + * Returns a flex_vector containing value `value` at position `index`. + * Undefined for `index >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: set/start + * :end-before: set/end + * + * @endrst + */ + flex_vector set(size_type index, value_type value) const& + { return impl_.assoc(index, std::move(value)); } + + decltype(auto) set(size_type index, value_type value) && + { return set_move(move_t{}, index, std::move(value)); } + + /*! + * Returns a vector containing the result of the expression + * `fn((*this)[idx])` at position `idx`. + * Undefined for `index >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: update/start + * :end-before: update/end + * + * @endrst + + */ + template + flex_vector update(size_type index, FnT&& fn) const& + { return impl_.update(index, std::forward(fn)); } + + template + decltype(auto) update(size_type index, FnT&& fn) && + { return update_move(move_t{}, index, std::forward(fn)); } + + /*! + * Returns a vector containing only the first `min(elems, size())` + * elements. It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: take/start + * :end-before: take/end + * + * @endrst + */ + flex_vector take(size_type elems) const& + { return impl_.take(elems); } + + decltype(auto) take(size_type elems) && + { return take_move(move_t{}, elems); } + + /*! + * Returns a vector without the first `min(elems, size())` + * elements. It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: drop/start + * :end-before: drop/end + * + * @endrst + */ + flex_vector drop(size_type elems) const& + { return impl_.drop(elems); } + + decltype(auto) drop(size_type elems) && + { return drop_move(move_t{}, elems); } + + /*! + * Concatenation operator. Returns a flex_vector with the contents + * of `l` followed by those of `r`. It may allocate memory + * and its complexity is @f$ O(log(max(size_r, size_l))) @f$ + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: concat/start + * :end-before: concat/end + * + * @endrst + */ + friend flex_vector operator+ (const flex_vector& l, const flex_vector& r) + { return l.impl_.concat(r.impl_); } + + friend decltype(auto) operator+ (flex_vector&& l, const flex_vector& r) + { return concat_move(move_t{}, std::move(l), r); } + + friend decltype(auto) operator+ (const flex_vector& l, flex_vector&& r) + { return concat_move(move_t{}, l, std::move(r)); } + + friend decltype(auto) operator+ (flex_vector&& l, flex_vector&& r) + { return concat_move(move_t{}, std::move(l), std::move(r)); } + + /*! + * Returns a flex_vector with the `value` inserted at index + * `pos`. It may allocate memory and its complexity is @f$ + * O(log(size)) @f$ + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: insert/start + * :end-before: insert/end + * + * @endrst + */ + flex_vector insert(size_type pos, T value) const& + { return take(pos).push_back(std::move(value)) + drop(pos); } + decltype(auto) insert(size_type pos, T value) && + { + using std::move; + auto rs = drop(pos); + return std::move(*this).take(pos).push_back( + std::move(value)) + std::move(rs); + } + + flex_vector insert(size_type pos, flex_vector value) const& + { return take(pos) + std::move(value) + drop(pos); } + decltype(auto) insert(size_type pos, flex_vector value) && + { + using std::move; + auto rs = drop(pos); + return std::move(*this).take(pos) + std::move(value) + std::move(rs); + } + + /*! + * Returns a flex_vector without the element at index `pos`. It + * may allocate memory and its complexity is @f$ O(log(size)) @f$ + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/flex-vector/flex-vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: erase/start + * :end-before: erase/end + * + * @endrst + */ + flex_vector erase(size_type pos) const& + { return take(pos) + drop(pos + 1); } + decltype(auto) erase(size_type pos) && + { + auto rs = drop(pos + 1); + return std::move(*this).take(pos) + std::move(rs); + } + + flex_vector erase(size_type pos, size_type lpos) const& + { return lpos > pos ? take(pos) + drop(lpos) : *this; } + decltype(auto) erase(size_type pos, size_type lpos) && + { + if (lpos > pos) { + auto rs = drop(lpos); + return std::move(*this).take(pos) + std::move(rs); + } else { + return std::move(*this); + } + } + + /*! + * Returns an @a transient form of this container, an + * `immer::flex_vector_transient`. + */ + transient_type transient() const& + { return transient_type{ impl_ }; } + transient_type transient() && + { return transient_type{ std::move(impl_) }; } + + // Semi-private + const impl_t& impl() const { return impl_; } + +#if IMMER_DEBUG_PRINT + void debug_print() const + { impl_.debug_print(); } +#endif + +private: + friend transient_type; + + flex_vector(impl_t impl) + : impl_(std::move(impl)) + { +#if IMMER_DEBUG_PRINT + // force the compiler to generate debug_print, so we can call + // it from a debugger + [](volatile auto){}(&flex_vector::debug_print); +#endif + } + + flex_vector&& push_back_move(std::true_type, value_type value) + { impl_.push_back_mut({}, std::move(value)); return std::move(*this); } + flex_vector push_back_move(std::false_type, value_type value) + { return impl_.push_back(std::move(value)); } + + flex_vector&& set_move(std::true_type, size_type index, value_type value) + { impl_.assoc_mut({}, index, std::move(value)); return std::move(*this); } + flex_vector set_move(std::false_type, size_type index, value_type value) + { return impl_.assoc(index, std::move(value)); } + + template + flex_vector&& update_move(std::true_type, size_type index, Fn&& fn) + { impl_.update_mut({}, index, std::forward(fn)); return std::move(*this); } + template + flex_vector update_move(std::false_type, size_type index, Fn&& fn) + { return impl_.update(index, std::forward(fn)); } + + flex_vector&& take_move(std::true_type, size_type elems) + { impl_.take_mut({}, elems); return std::move(*this); } + flex_vector take_move(std::false_type, size_type elems) + { return impl_.take(elems); } + + flex_vector&& drop_move(std::true_type, size_type elems) + { impl_.drop_mut({}, elems); return std::move(*this); } + flex_vector drop_move(std::false_type, size_type elems) + { return impl_.drop(elems); } + + static flex_vector&& concat_move(std::true_type, flex_vector&& l, const flex_vector& r) + { concat_mut_l(l.impl_, {}, r.impl_); return std::move(l); } + static flex_vector&& concat_move(std::true_type, const flex_vector& l, flex_vector&& r) + { concat_mut_r(l.impl_, r.impl_, {}); return std::move(r); } + static flex_vector&& concat_move(std::true_type, flex_vector&& l, flex_vector&& r) + { concat_mut_lr_l(l.impl_, {}, r.impl_, {}); return std::move(l); } + static flex_vector concat_move(std::false_type, const flex_vector& l, const flex_vector& r) + { return l.impl_.concat(r.impl_); } + + impl_t impl_ = impl_t::empty; +}; + +} // namespace immer diff --git a/src/immer/flex_vector_transient.hpp b/src/immer/flex_vector_transient.hpp new file mode 100644 index 000000000000..19c088d9bd10 --- /dev/null +++ b/src/immer/flex_vector_transient.hpp @@ -0,0 +1,232 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +namespace immer { + +template +class flex_vector; + +template +class vector_transient; + +/*! + * Mutable version of `immer::flex_vector`. + * + * @rst + * + * Refer to :doc:`transients` to learn more about when and how to use + * the mutable versions of immutable containers. + * + * @endrst + */ +template > +class flex_vector_transient + : MemoryPolicy::transience_t::owner +{ + using impl_t = detail::rbts::rrbtree; + using base_t = typename MemoryPolicy::transience_t::owner; + using owner_t = typename MemoryPolicy::transience_t::owner; + +public: + static constexpr auto bits = B; + static constexpr auto bits_leaf = BL; + using memory_policy = MemoryPolicy; + + using value_type = T; + using reference = const T&; + using size_type = detail::rbts::size_t; + using difference_type = std::ptrdiff_t; + using const_reference = const T&; + + using iterator = detail::rbts::rrbtree_iterator; + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + + using persistent_type = flex_vector; + + /*! + * Default constructor. It creates a flex_vector of `size() == 0`. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + flex_vector_transient() = default; + + /*! + * Default constructor. It creates a flex_vector with the same + * contents as `v`. It does not allocate memory and is + * @f$ O(1) @f$. + */ + flex_vector_transient(vector_transient v) + : base_t { std::move(static_cast(v)) } + , impl_ { v.impl_.size, v.impl_.shift, + v.impl_.root->inc(), v.impl_.tail->inc() } + {} + + /*! + * Returns an iterator pointing at the first element of the + * collection. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + iterator begin() const { return {impl_}; } + + /*! + * Returns an iterator pointing just after the last element of the + * collection. It does not allocate and its complexity is @f$ O(1) @f$. + */ + iterator end() const { return {impl_, typename iterator::end_t{}}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing at the first element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing after the last element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rend() const { return reverse_iterator{begin()}; } + + /*! + * Returns the number of elements in the container. It does + * not allocate memory and its complexity is @f$ O(1) @f$. + */ + size_type size() const { return impl_.size; } + + /*! + * Returns `true` if there are no elements in the container. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + bool empty() const { return impl_.size == 0; } + + /*! + * Returns a `const` reference to the element at position `index`. + * It is undefined when @f$ 0 index \geq size() @f$. It does not + * allocate memory and its complexity is *effectively* @f$ O(1) + * @f$. + */ + reference operator[] (size_type index) const + { return impl_.get(index); } + + /*! + * Returns a `const` reference to the element at position + * `index`. It throws an `std::out_of_range` exception when @f$ + * index \geq size() @f$. It does not allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + reference at(size_type index) const + { return impl_.get_check(index); } + + /*! + * Inserts `value` at the end. It may allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + void push_back(value_type value) + { impl_.push_back_mut(*this, std::move(value)); } + + /*! + * Sets to the value `value` at position `idx`. + * Undefined for `index >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + void set(size_type index, value_type value) + { impl_.assoc_mut(*this, index, std::move(value)); } + + /*! + * Updates the vector to contain the result of the expression + * `fn((*this)[idx])` at position `idx`. + * Undefined for `0 >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + template + void update(size_type index, FnT&& fn) + { impl_.update_mut(*this, index, std::forward(fn)); } + + /*! + * Resizes the vector to only contain the first `min(elems, size())` + * elements. It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + void take(size_type elems) + { impl_.take_mut(*this, elems); } + + /*! + * Removes the first the first `min(elems, size())` + * elements. It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + void drop(size_type elems) + { impl_.drop_mut(*this, elems); } + + /*! + * Returns an @a immutable form of this container, an + * `immer::flex_vector`. + */ + persistent_type persistent() & + { + this->owner_t::operator=(owner_t{}); + return persistent_type{ impl_ }; + } + persistent_type persistent() && + { return persistent_type{ std::move(impl_) }; } + + /*! + * Appends the contents of the `r` at the end. It may allocate + * memory and its complexity is: + * @f$ O(log(max(size_r, size_l))) @f$ + */ + void append(flex_vector_transient& r) + { + r.owner_t::operator=(owner_t{}); + concat_mut_l(impl_, *this, r.impl_); + } + void append(flex_vector_transient&& r) + { concat_mut_lr_l(impl_, *this, r.impl_, r); } + + /*! + * Prepends the contents of the `l` at the beginning. It may + * allocate memory and its complexity is: + * @f$ O(log(max(size_r, size_l))) @f$ + */ + void prepend(flex_vector_transient& l) + { + l.owner_t::operator=(owner_t{}); + concat_mut_r(l.impl_, impl_, *this); + } + void prepend(flex_vector_transient&& l) + { concat_mut_lr_r(l.impl_, l, impl_, *this); } + +private: + friend persistent_type; + + flex_vector_transient(impl_t impl) + : impl_(std::move(impl)) + {} + + impl_t impl_ = impl_t::empty; +}; + +} // namespace immer diff --git a/src/immer/heap/cpp_heap.hpp b/src/immer/heap/cpp_heap.hpp new file mode 100644 index 000000000000..cd129b406bea --- /dev/null +++ b/src/immer/heap/cpp_heap.hpp @@ -0,0 +1,41 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { + +/*! + * A heap that uses `operator new` and `operator delete`. + */ +struct cpp_heap +{ + /*! + * Returns a pointer to a memory region of size `size`, if the + * allocation was successful, and throws otherwise. + */ + template + static void* allocate(std::size_t size, Tags...) + { + return ::operator new(size); + } + + /*! + * Releases a memory region `data` that was previously returned by + * `allocate`. One must not use nor deallocate again a memory + * region that once it has been deallocated. + */ + static void deallocate(std::size_t size, void* data) + { + ::operator delete(data); + } +}; + +} // namespace immer diff --git a/src/immer/heap/debug_size_heap.hpp b/src/immer/heap/debug_size_heap.hpp new file mode 100644 index 000000000000..124beb81b419 --- /dev/null +++ b/src/immer/heap/debug_size_heap.hpp @@ -0,0 +1,50 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +namespace immer { + +#if IMMER_ENABLE_DEBUG_SIZE_HEAP + +/*! + * A heap that in debug mode ensures that the sizes for allocation and + * deallocation do match. + */ +template +struct debug_size_heap +{ + template + static void* allocate(std::size_t size, Tags... tags) + { + auto p = (std::size_t*) Base::allocate(size + sizeof(std::size_t), tags...); + *p = size; + return p + 1; + } + + template + static void deallocate(std::size_t size, void* data, Tags... tags) + { + auto p = ((std::size_t*) data) - 1; + assert(*p == size); + Base::deallocate(size + sizeof(std::size_t), p, tags...); + } +}; + +#else // IMMER_ENABLE_DEBUG_SIZE_HEAP + +template +using debug_size_heap = identity_heap; + +#endif // !IMMER_ENABLE_DEBUG_SIZE_HEAP + +} // namespace immer diff --git a/src/immer/heap/free_list_heap.hpp b/src/immer/heap/free_list_heap.hpp new file mode 100644 index 000000000000..d82d0967b00c --- /dev/null +++ b/src/immer/heap/free_list_heap.hpp @@ -0,0 +1,83 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +#include +#include + +namespace immer { + +/*! + * Adaptor that does not release the memory to the parent heap but + * instead it keeps the memory in a thread-safe global free list. Must + * be preceded by a `with_data` heap adaptor. + * + * @tparam Size Maximum size of the objects to be allocated. + * @tparam Base Type of the parent heap. + */ +template +struct free_list_heap : Base +{ + using base_t = Base; + + template + static void* allocate(std::size_t size, Tags...) + { + assert(size <= sizeof(free_list_node) + Size); + assert(size >= sizeof(free_list_node)); + + free_list_node* n; + do { + n = head().data; + if (!n) { + auto p = base_t::allocate(Size + sizeof(free_list_node)); + return static_cast(p); + } + } while (!head().data.compare_exchange_weak(n, n->next)); + head().count.fetch_sub(1u, std::memory_order_relaxed); + return n; + } + + template + static void deallocate(std::size_t size, void* data, Tags...) + { + assert(size <= sizeof(free_list_node) + Size); + assert(size >= sizeof(free_list_node)); + + // we use relaxed, because we are fine with temporarily having + // a few more/less buffers in free list + if (head().count.load(std::memory_order_relaxed) >= Limit) { + base_t::deallocate(Size + sizeof(free_list_node), data); + } else { + auto n = static_cast(data); + do { + n->next = head().data; + } while (!head().data.compare_exchange_weak(n->next, n)); + head().count.fetch_add(1u, std::memory_order_relaxed); + } + } + +private: + struct head_t + { + std::atomic data; + std::atomic count; + }; + + static head_t& head() + { + static head_t head_{{nullptr}, {0}}; + return head_; + } +}; + +} // namespace immer diff --git a/src/immer/heap/free_list_node.hpp b/src/immer/heap/free_list_node.hpp new file mode 100644 index 000000000000..5c2f5b7529d9 --- /dev/null +++ b/src/immer/heap/free_list_node.hpp @@ -0,0 +1,25 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { + +struct free_list_node +{ + free_list_node* next; +}; + +template +struct with_free_list_node + : with_data +{}; + +} // namespace immer diff --git a/src/immer/heap/gc_heap.hpp b/src/immer/heap/gc_heap.hpp new file mode 100644 index 000000000000..f37da2f6e3dd --- /dev/null +++ b/src/immer/heap/gc_heap.hpp @@ -0,0 +1,130 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +#if IMMER_HAS_LIBGC +#include +#else +#error "Using garbage collection requires libgc" +#endif + +#include +#include + +namespace immer { + +#ifdef __APPLE__ +#define IMMER_GC_REQUIRE_INIT 1 +#else +#define IMMER_GC_REQUIRE_INIT 0 +#endif + +#if IMMER_GC_REQUIRE_INIT + +namespace detail { + +template +struct gc_initializer +{ + gc_initializer() { GC_init(); } + static gc_initializer init; +}; + +template +gc_initializer gc_initializer::init {}; + +inline void gc_initializer_guard() +{ + static gc_initializer<> init_ = gc_initializer<>::init; + (void) init_; +} + +} // namespace detail + +#define IMMER_GC_INIT_GUARD_ ::immer::detail::gc_initializer_guard() + +#else + +#define IMMER_GC_INIT_GUARD_ + +#endif // IMMER_GC_REQUIRE_INIT + +/*! + * Heap that uses a tracing garbage collector. + * + * @rst + * + * This heap uses the `Boehm's conservative garbage collector`_ under + * the hood. This is a tracing garbage collector that automatically + * reclaims unused memory. Thus, it is not needed to call + * ``deallocate()`` in order to release memory. + * + * .. admonition:: Dependencies + * :class: tip + * + * In order to use this header file, you need to make sure that + * Boehm's ``libgc`` is your include path and link to its binary + * library. + * + * .. caution:: Memory that is allocated with the standard ``malloc`` + * and ``free`` is not visible to ``libgc`` when it is looking for + * references. This means that if, let's say, you store a + * :cpp:class:`immer::vector` using a ``gc_heap`` inside a + * ``std::vector`` that uses a standard allocator, the memory of + * the former might be released automatically at unexpected times + * causing crashes. + * + * .. caution:: When using a ``gc_heap`` in combination with immutable + * containers, the destructors of the contained objects will never + * be called. It is ok to store containers inside containers as + * long as all of them use a ``gc_heap`` consistently, but storing + * other kinds of objects with relevant destructors + * (e.g. containers with reference counting or other kinds of + * *resource handles*) might cause memory leaks and other problems. + * + * .. _boehm's conservative garbage collector: https://github.com/ivmai/bdwgc + * + * @endrst + */ +class gc_heap +{ +public: + static void* allocate(std::size_t n) + { + IMMER_GC_INIT_GUARD_; + auto p = GC_malloc(n); + if (IMMER_UNLIKELY(!p)) + throw std::bad_alloc{}; + return p; + } + + static void* allocate(std::size_t n, norefs_tag) + { + IMMER_GC_INIT_GUARD_; + auto p = GC_malloc_atomic(n); + if (IMMER_UNLIKELY(!p)) + throw std::bad_alloc{}; + return p; + } + + static void deallocate(std::size_t, void* data) + { + GC_free(data); + } + + static void deallocate(std::size_t, void* data, norefs_tag) + { + GC_free(data); + } +}; + +} // namespace immer diff --git a/src/immer/heap/heap_policy.hpp b/src/immer/heap/heap_policy.hpp new file mode 100644 index 000000000000..a0723cbcf2b9 --- /dev/null +++ b/src/immer/heap/heap_policy.hpp @@ -0,0 +1,148 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace immer { + +/*! + * Heap policy that unconditionally uses its `Heap` argument. + */ +template +struct heap_policy +{ + using type = Heap; + + template + struct optimized + { + using type = Heap; + }; +}; + +template +struct enable_optimized_heap_policy +{ + static void* operator new (std::size_t size) + { + using heap_type = typename HeapPolicy + ::template optimized::type; + + return heap_type::allocate(size); + } + + static void operator delete (void* data, std::size_t size) + { + using heap_type = typename HeapPolicy + ::template optimized::type; + + heap_type::deallocate(size, data); + } +}; + +/*! + * Heap policy that returns a heap with a free list of objects + * of `max_size = max(Sizes...)` on top an underlying `Heap`. Note + * these two properties of the resulting heap: + * + * - Allocating an object that is bigger than `max_size` may trigger + * *undefined behavior*. + * + * - Allocating an object of size less than `max_size` still + * returns an object of `max_size`. + * + * Basically, this heap will always return objects of `max_size`. + * When an object is freed, it does not directly invoke `std::free`, + * but it keeps the object in a global linked list instead. When a + * new object is requested, it does not need to call `std::malloc` but + * it can directly pop and return the other object from the global + * list, a much faster operation. + * + * This actually creates a hierarchy with two free lists: + * + * - A `thread_local` free list is used first. It does not need any + * kind of synchronization and is very fast. When the thread + * finishes, its contents are returned to the next free list. + * + * - A global free list using lock-free access via atomics. + * + * @tparam Heap Heap to be used when the free list is empty. + * + * @rst + * + * .. tip:: For many applications that use immutable data structures + * significantly, this is actually the best heap policy, and it + * might become the default in the future. + * + * Note that most our data structures internally use trees with the + * same big branching factors. This means that all *vectors*, + * *maps*, etc. can just allocate elements from the same free-list + * optimized heap. Not only does this lowers the allocation time, + * but also makes up for more efficient *cache utilization*. When + * a new node is needed, there are high chances the allocator will + * return a node that was just accessed. When batches of immutable + * updates are made, this can make a significant difference. + * + * @endrst + */ +template +struct free_list_heap_policy +{ + using type = debug_size_heap; + + template + struct optimized + { + using type = split_heap< + Size, + with_free_list_node< + thread_local_free_list_heap< + Size, + Limit, + free_list_heap< + Size, Limit, + debug_size_heap>>>, + debug_size_heap>; + }; +}; + +/*! + * Similar to @ref free_list_heap_policy, but it assumes no + * multi-threading, so a single global free list with no concurrency + * checks is used. + */ +template +struct unsafe_free_list_heap_policy +{ + using type = Heap; + + template + struct optimized + { + using type = split_heap< + Size, + with_free_list_node< + unsafe_free_list_heap< + Size, Limit, + debug_size_heap>>, + debug_size_heap>; + }; +}; + +} // namespace immer diff --git a/src/immer/heap/identity_heap.hpp b/src/immer/heap/identity_heap.hpp new file mode 100644 index 000000000000..032cb3f221d0 --- /dev/null +++ b/src/immer/heap/identity_heap.hpp @@ -0,0 +1,34 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { + +/*! + * A heap that simply passes on to the parent heap. + */ +template +struct identity_heap : Base +{ + template + static void* allocate(std::size_t size, Tags... tags) + { + return Base::allocate(size, tags...); + } + + template + static void deallocate(std::size_t size, void* data, Tags... tags) + { + Base::deallocate(size, data, tags...); + } +}; + +} // namespace immer diff --git a/src/immer/heap/malloc_heap.hpp b/src/immer/heap/malloc_heap.hpp new file mode 100644 index 000000000000..73909058dec3 --- /dev/null +++ b/src/immer/heap/malloc_heap.hpp @@ -0,0 +1,47 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +#include +#include + +namespace immer { + +/*! + * A heap that uses `std::malloc` and `std::free` to manage memory. + */ +struct malloc_heap +{ + /*! + * Returns a pointer to a memory region of size `size`, if the + * allocation was successful and throws `std::bad_alloc` otherwise. + */ + template + static void* allocate(std::size_t size, Tags...) + { + auto p = std::malloc(size); + if (IMMER_UNLIKELY(!p)) + throw std::bad_alloc{}; + return p; + } + + /*! + * Releases a memory region `data` that was previously returned by + * `allocate`. One must not use nor deallocate again a memory + * region that once it has been deallocated. + */ + static void deallocate(std::size_t, void* data) + { + std::free(data); + } +}; + +} // namespace immer diff --git a/src/immer/heap/split_heap.hpp b/src/immer/heap/split_heap.hpp new file mode 100644 index 000000000000..8ce210815f4c --- /dev/null +++ b/src/immer/heap/split_heap.hpp @@ -0,0 +1,41 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +namespace immer { + +/*! + * Adaptor that uses `SmallHeap` for allocations that are smaller or + * equal to `Size` and `BigHeap` otherwise. + */ +template +struct split_heap +{ + template + static void* allocate(std::size_t size, Tags... tags) + { + return size <= Size + ? SmallHeap::allocate(size, tags...) + : BigHeap::allocate(size, tags...); + } + + template + static void deallocate(std::size_t size, void* data, Tags... tags) + { + if (size <= Size) + SmallHeap::deallocate(size, data, tags...); + else + BigHeap::deallocate(size, data, tags...); + } +}; + +} // namespace immer diff --git a/src/immer/heap/tags.hpp b/src/immer/heap/tags.hpp new file mode 100644 index 000000000000..a3012bd3ef35 --- /dev/null +++ b/src/immer/heap/tags.hpp @@ -0,0 +1,15 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +namespace immer { + +struct norefs_tag {}; + +} // namespace immer diff --git a/src/immer/heap/thread_local_free_list_heap.hpp b/src/immer/heap/thread_local_free_list_heap.hpp new file mode 100644 index 000000000000..2539ce73c102 --- /dev/null +++ b/src/immer/heap/thread_local_free_list_heap.hpp @@ -0,0 +1,55 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { +namespace detail { + +template +struct thread_local_free_list_storage +{ + struct head_t + { + free_list_node* data; + std::size_t count; + + ~head_t() { Heap::clear(); } + }; + + static head_t& head() + { + thread_local static head_t head_{nullptr, 0}; + return head_; + } +}; + +} // namespace detail + +/*! + * Adaptor that does not release the memory to the parent heap but + * instead it keeps the memory in a `thread_local` global free + * list. Must be preceded by a `with_data` heap + * adaptor. When the current thread finishes, the memory is returned + * to the parent heap. + * + * @tparam Size Maximum size of the objects to be allocated. + * @tparam Limit Maximum number of elements to keep in the free list. + * @tparam Base Type of the parent heap. + */ +template +struct thread_local_free_list_heap : detail::unsafe_free_list_heap_impl< + detail::thread_local_free_list_storage, + Size, + Limit, + Base> +{}; + +} // namespace immer diff --git a/src/immer/heap/unsafe_free_list_heap.hpp b/src/immer/heap/unsafe_free_list_heap.hpp new file mode 100644 index 000000000000..9a1fdd73e44d --- /dev/null +++ b/src/immer/heap/unsafe_free_list_heap.hpp @@ -0,0 +1,108 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +namespace immer { +namespace detail { + +template +struct unsafe_free_list_storage +{ + struct head_t + { + free_list_node* data; + std::size_t count; + }; + + static head_t& head() + { + static head_t head_ {nullptr, 0}; + return head_; + } +}; + +template class Storage, + std::size_t Size, + std::size_t Limit, + typename Base> +class unsafe_free_list_heap_impl : Base +{ + using storage = Storage; + +public: + using base_t = Base; + + template + static void* allocate(std::size_t size, Tags...) + { + assert(size <= sizeof(free_list_node) + Size); + assert(size >= sizeof(free_list_node)); + + auto n = storage::head().data; + if (!n) { + auto p = base_t::allocate(Size + sizeof(free_list_node)); + return static_cast(p); + } + --storage::head().count; + storage::head().data = n->next; + return n; + } + + template + static void deallocate(std::size_t size, void* data, Tags...) + { + assert(size <= sizeof(free_list_node) + Size); + assert(size >= sizeof(free_list_node)); + + if (storage::head().count >= Limit) + base_t::deallocate(Size + sizeof(free_list_node), data); + else { + auto n = static_cast(data); + n->next = storage::head().data; + storage::head().data = n; + ++storage::head().count; + } + } + + static void clear() + { + while (storage::head().data) { + auto n = storage::head().data->next; + base_t::deallocate(Size + sizeof(free_list_node), storage::head().data); + storage::head().data = n; + --storage::head().count; + } + } +}; + +} // namespace detail + +/*! + * Adaptor that does not release the memory to the parent heap but + * instead it keeps the memory in a global free list that **is not + * thread-safe**. Must be preceded by a `with_data` heap adaptor. + * + * @tparam Size Maximum size of the objects to be allocated. + * @tparam Limit Maximum number of elements to keep in the free list. + * @tparam Base Type of the parent heap. + */ +template +struct unsafe_free_list_heap : detail::unsafe_free_list_heap_impl< + detail::unsafe_free_list_storage, + Size, + Limit, + Base> +{}; + +} // namespace immer diff --git a/src/immer/heap/with_data.hpp b/src/immer/heap/with_data.hpp new file mode 100644 index 000000000000..1e8c2abb082e --- /dev/null +++ b/src/immer/heap/with_data.hpp @@ -0,0 +1,43 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { + +/*! + * Appends a default constructed extra object of type `T` at the + * *before* the requested region. + * + * @tparam T Type of the appended data. + * @tparam Base Type of the parent heap. + */ +template +struct with_data : Base +{ + using base_t = Base; + + template + static void* allocate(std::size_t size, Tags... tags) + { + auto p = base_t::allocate(size + sizeof(T), tags...); + return new (p) T{} + 1; + } + + template + static void deallocate(std::size_t size, void* p, Tags... tags) + { + auto dp = static_cast(p) - 1; + dp->~T(); + base_t::deallocate(size + sizeof(T), dp, tags...); + } +}; + +} // namespace immer diff --git a/src/immer/map.hpp b/src/immer/map.hpp new file mode 100644 index 000000000000..553bfcd54495 --- /dev/null +++ b/src/immer/map.hpp @@ -0,0 +1,311 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +#include + +namespace immer { + +template +class map_transient; + +/*! + * Immutable unordered mapping of values from type `K` to type `T`. + * + * @tparam K The type of the keys. + * @tparam T The type of the values to be stored in the container. + * @tparam Hash The type of a function object capable of hashing + * values of type `T`. + * @tparam Equal The type of a function object capable of comparing + * values of type `T`. + * @tparam MemoryPolicy Memory management policy. See @ref + * memory_policy. + * + * @rst + * + * This cotainer provides a good trade-off between cache locality, + * search, update performance and structural sharing. It does so by + * storing the data in contiguous chunks of :math:`2^{B}` elements. + * When storing big objects, the size of these contiguous chunks can + * become too big, damaging performance. If this is measured to be + * problematic for a specific use-case, it can be solved by using a + * `immer::box` to wrap the type `T`. + * + * **Example** + * .. literalinclude:: ../example/map/intro.cpp + * :language: c++ + * :start-after: intro/start + * :end-before: intro/end + * + * @endrst + * + */ +template , + typename Equal = std::equal_to, + typename MemoryPolicy = default_memory_policy, + detail::hamts::bits_t B = default_bits> +class map +{ + using value_t = std::pair; + + struct project_value + { + const T& operator() (const value_t& v) const noexcept + { + return v.second; + } + }; + + struct project_value_ptr + { + const T* operator() (const value_t& v) const noexcept + { + return &v.second; + } + }; + + struct combine_value + { + template + value_t operator() (Kf&& k, Tf&& v) const + { + return { std::forward(k), std::forward(v) }; + } + }; + + struct default_value + { + const T& operator() () const + { + static T v{}; + return v; + } + }; + + struct error_value + { + const T& operator() () const + { + throw std::out_of_range{"key not found"}; + } + }; + + struct hash_key + { + auto operator() (const value_t& v) + { return Hash{}(v.first); } + + auto operator() (const K& v) + { return Hash{}(v); } + }; + + struct equal_key + { + auto operator() (const value_t& a, const value_t& b) + { return Equal{}(a.first, b.first); } + + auto operator() (const value_t& a, const K& b) + { return Equal{}(a.first, b); } + }; + + struct equal_value + { + auto operator() (const value_t& a, const value_t& b) + { return Equal{}(a.first, b.first) && a.second == b.second; } + }; + + using impl_t = detail::hamts::champ< + value_t, hash_key, equal_key, MemoryPolicy, B>; + +public: + using key_type = K; + using mapped_type = T; + using value_type = std::pair; + using size_type = detail::hamts::size_t; + using diference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = Equal; + using reference = const value_type&; + using const_reference = const value_type&; + + using iterator = detail::hamts::champ_iterator< + value_t, hash_key, equal_key, MemoryPolicy, B>; + using const_iterator = iterator; + + using transient_type = map_transient; + + /*! + * Default constructor. It creates a set of `size() == 0`. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + map() = default; + + /*! + * Returns an iterator pointing at the first element of the + * collection. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + iterator begin() const { return {impl_}; } + + /*! + * Returns an iterator pointing just after the last element of the + * collection. It does not allocate and its complexity is @f$ O(1) @f$. + */ + iterator end() const { return {impl_, typename iterator::end_t{}}; } + + /*! + * Returns the number of elements in the container. It does + * not allocate memory and its complexity is @f$ O(1) @f$. + */ + size_type size() const { return impl_.size; } + + /*! + * Returns `1` when the key `k` is contained in the map or `0` + * otherwise. It won't allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + size_type count(const K& k) const + { return impl_.template get, + detail::constantly>(k); } + + /*! + * Returns a `const` reference to the values associated to the key + * `k`. If the key is not contained in the map, it returns a + * default constructed value. It does not allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + const T& operator[] (const K& k) const + { return impl_.template get(k); } + + /*! + * Returns a `const` reference to the values associated to the key + * `k`. If the key is not contained in the map, throws an + * `std::out_of_range` error. It does not allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + const T& at(const K& k) const + { return impl_.template get(k); } + + + /*! + * Returns a pointer to the value associated with the key `k`. If + * the key is not contained in the map, a `nullptr` is returned. + * It does not allocate memory and its complexity is *effectively* + * @f$ O(1) @f$. + * + * @rst + * + * .. admonition:: Why doesn't this function return an iterator? + * + * Associative containers from the C++ standard library provide a + * ``find`` method that returns an iterator pointing to the + * element in the container or ``end()`` when the key is missing. + * In the case of an unordered container, the only meaningful + * thing one may do with it is to compare it with the end, to + * test if the find was succesfull, and dereference it. This + * comparison is cumbersome compared to testing for a non-empty + * optional value. Furthermore, for an immutable container, + * returning an iterator would have some additional performance + * cost, with no benefits otherwise. + * + * In our opinion, this function should return a + * ``std::optional`` but this construction is not valid + * in any current standard. As a compromise we return a + * pointer, which has similar syntactic properties yet it is + * unfortunatelly unnecessarily unrestricted. + * + * @endrst + */ + const T* find(const K& k) const + { return impl_.template get>(k); } + + /*! + * Returns whether the sets are equal. + */ + bool operator==(const map& other) const + { return impl_.template equals(other.impl_); } + bool operator!=(const map& other) const + { return !(*this == other); } + + /*! + * Returns a map containing the association `value`. If the key is + * already in the map, it replaces its association in the map. + * It may allocate memory and its complexity is *effectively* @f$ + * O(1) @f$. + */ + map insert(value_type value) const + { return impl_.add(std::move(value)); } + + /*! + * Returns a map containing the association `(k, v)`. If the key + * is already in the map, it replaces its association in the map. + * It may allocate memory and its complexity is *effectively* @f$ + * O(1) @f$. + */ + map set(key_type k, mapped_type v) const + { return impl_.add({std::move(k), std::move(v)}); } + + /*! + * Returns a map replacing the association `(k, v)` by the + * association new association `(k, fn(v))`, where `v` is the + * currently associated value for `k` in the map or a default + * constructed value otherwise. It may allocate memory + * and its complexity is *effectively* @f$ O(1) @f$. + */ + template + map update(key_type k, Fn&& fn) const + { + return impl_ + .template update( + std::move(k), std::forward(fn)); + } + + /*! + * Returns a map without the key `k`. If the key is not + * associated in the map it returns the same map. It may allocate + * memory and its complexity is *effectively* @f$ O(1) @f$. + */ + map erase(const K& k) const + { return impl_.sub(k); } + + /*! + * Returns an @a transient form of this container, a + * `immer::map_transient`. + */ + transient_type transient() const& + { return transient_type{ impl_ }; } + transient_type transient() && + { return transient_type{ std::move(impl_) }; } + + // Semi-private + const impl_t& impl() const { return impl_; } + +private: + friend transient_type; + + map(impl_t impl) + : impl_(std::move(impl)) + {} + + impl_t impl_ = impl_t::empty; +}; + +} // namespace immer diff --git a/src/immer/map_transient.hpp b/src/immer/map_transient.hpp new file mode 100644 index 000000000000..542dac1d5deb --- /dev/null +++ b/src/immer/map_transient.hpp @@ -0,0 +1,29 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +#include + +namespace immer { + +/*! + * **WORK IN PROGRESS** + */ +template , + typename Equal = std::equal_to, + typename MemoryPolicy = default_memory_policy, + detail::hamts::bits_t B = default_bits> +class map_transient; + +} // namespace immer diff --git a/src/immer/memory_policy.hpp b/src/immer/memory_policy.hpp new file mode 100644 index 000000000000..9d95f56a0e48 --- /dev/null +++ b/src/immer/memory_policy.hpp @@ -0,0 +1,138 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace immer { + +/*! + * Metafunction that returns the best *transience policy* to use for a + * given *refcount policy*. + */ +template +struct get_transience_policy + : std::conditional::value, + gc_transience_policy, + no_transience_policy> +{}; + +template +using get_transience_policy_t = typename get_transience_policy::type; + +/*! + * Metafunction that returns wether to *prefer fewer bigger objects* + * to use for a given *heap policy*. + */ +template +struct get_prefer_fewer_bigger_objects + : std::integral_constant + >::value> +{}; + +template +constexpr auto get_prefer_fewer_bigger_objects_v = + get_prefer_fewer_bigger_objects::value; + +/*! + * Metafunction that returns wether to use *transient R-Values* + * for a given *refcount policy*. + */ +template +struct get_use_transient_rvalues + : std::integral_constant::value> +{}; + +template +constexpr auto get_use_transient_rvalues_v = get_use_transient_rvalues::value; + +/*! + * This is a default implementation of a *memory policy*. A memory + * policy is just a bag of other policies plus some flags with hints + * to the user about the best way to use these strategies. + * + * @tparam HeapPolicy A *heap policy*, for example, @ref heap_policy. + * @tparam RefcountPolicy A *reference counting policy*, for example, + * @ref refcount_policy. + * @tparam TransiencePolicy A *transience policy*, for example, + * @ref no_transience_policy. + * @tparam PreferFewerBiggerObjects Boolean flag indicating whether + * the user should prefer to allocate memory in bigger chungs + * --e.g. by putting various objects in the same memory + * region-- or not. + * @tparam UseTransientRValues Boolean flag indicating whether + * immutable containers should try to modify contents in-place + * when manipulating an r-value reference. + */ +template , + bool PreferFewerBiggerObjects = get_prefer_fewer_bigger_objects_v, + bool UseTransientRValues = get_use_transient_rvalues_v> +struct memory_policy +{ + using heap = HeapPolicy; + using refcount = RefcountPolicy; + using transience = TransiencePolicy; + + static constexpr bool prefer_fewer_bigger_objects = + PreferFewerBiggerObjects; + + static constexpr bool use_transient_rvalues = + UseTransientRValues; + + using transience_t = typename transience::template apply::type; +}; + +/*! + * The default *heap policy* just uses the standard heap with a + * @ref free_list_heap_policy. If `IMMER_NO_FREE_LIST` is defined to `1` + * then it just uses the standard heap. + */ +#if IMMER_NO_FREE_LIST +using default_heap_policy = heap_policy>; +#else +#if IMMER_NO_THREAD_SAFETY +using default_heap_policy = unsafe_free_list_heap_policy; +#else +using default_heap_policy = free_list_heap_policy; +#endif +#endif + +/*! + * By default we use thread safe reference counting. + */ +#if IMMER_NO_THREAD_SAFETY +using default_refcount_policy = unsafe_refcount_policy; +#else +using default_refcount_policy = refcount_policy; +#endif + +/*! + * The default memory policy. + */ +using default_memory_policy = memory_policy< + default_heap_policy, + default_refcount_policy>; + +} // namespace immer diff --git a/src/immer/refcount/enable_intrusive_ptr.hpp b/src/immer/refcount/enable_intrusive_ptr.hpp new file mode 100644 index 000000000000..1185a219fd9b --- /dev/null +++ b/src/immer/refcount/enable_intrusive_ptr.hpp @@ -0,0 +1,37 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +namespace immer { + +template +class enable_intrusive_ptr +{ + mutable RefcountPolicy refcount_data_; + +public: + enable_intrusive_ptr() + : refcount_data_{disowned{}} + {} + + friend void intrusive_ptr_add_ref(const Deriv* x) + { + x->refcount_data_.inc(); + } + + friend void intrusive_ptr_release(const Deriv* x) + { + if (x->refcount_data_.dec()) + delete x; + } +}; + +} // namespace immer diff --git a/src/immer/refcount/no_refcount_policy.hpp b/src/immer/refcount/no_refcount_policy.hpp new file mode 100644 index 000000000000..188fb9f5594e --- /dev/null +++ b/src/immer/refcount/no_refcount_policy.hpp @@ -0,0 +1,30 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +namespace immer { + +struct disowned {}; + +/*! + * Disables reference counting, to be used with an alternative garbage + * collection strategy like a `gc_heap`. + */ +struct no_refcount_policy +{ + no_refcount_policy() {}; + no_refcount_policy(disowned) {} + + void inc() {} + bool dec() { return false; } + void dec_unsafe() {} + bool unique() { return false; } +}; + +} // namespace immer diff --git a/src/immer/refcount/refcount_policy.hpp b/src/immer/refcount/refcount_policy.hpp new file mode 100644 index 000000000000..b143cbf7e43a --- /dev/null +++ b/src/immer/refcount/refcount_policy.hpp @@ -0,0 +1,52 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +#include +#include +#include + +namespace immer { + +/*! + * A reference counting policy implemented using an *atomic* `int` + * count. It is **thread-safe**. + */ +struct refcount_policy +{ + mutable std::atomic refcount; + + refcount_policy() : refcount{1} {}; + refcount_policy(disowned) : refcount{0} {} + + void inc() + { + refcount.fetch_add(1, std::memory_order_relaxed); + } + + bool dec() + { + return 1 == refcount.fetch_sub(1, std::memory_order_acq_rel); + } + + void dec_unsafe() + { + assert(refcount.load() > 1); + refcount.fetch_sub(1, std::memory_order_relaxed); + } + + bool unique() + { + return refcount == 1; + } +}; + +} // namespace immer diff --git a/src/immer/refcount/unsafe_refcount_policy.hpp b/src/immer/refcount/unsafe_refcount_policy.hpp new file mode 100644 index 000000000000..9a77f06a512a --- /dev/null +++ b/src/immer/refcount/unsafe_refcount_policy.hpp @@ -0,0 +1,35 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +#include +#include + +namespace immer { + +/*! + * A reference counting policy implemented using a raw `int` count. + * It is **not thread-safe**. + */ +struct unsafe_refcount_policy +{ + mutable int refcount; + + unsafe_refcount_policy() : refcount{1} {}; + unsafe_refcount_policy(disowned) : refcount{0} {} + + void inc() { ++refcount; } + bool dec() { return --refcount == 0; } + void dec_unsafe() { --refcount; } + bool unique() { return refcount == 1; } +}; + +} // namespace immer diff --git a/src/immer/set.hpp b/src/immer/set.hpp new file mode 100644 index 000000000000..0de09614d3d5 --- /dev/null +++ b/src/immer/set.hpp @@ -0,0 +1,161 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +#include + +namespace immer { + +template +class set_transient; + +/*! + * Immutable set representing an unordered bag of values. + * + * @tparam T The type of the values to be stored in the container. + * @tparam Hash The type of a function object capable of hashing + * values of type `T`. + * @tparam Equal The type of a function object capable of comparing + * values of type `T`. + * @tparam MemoryPolicy Memory management policy. See @ref + * memory_policy. + * + * @rst + * + * This cotainer provides a good trade-off between cache locality, + * membership checks, update performance and structural sharing. It + * does so by storing the data in contiguous chunks of :math:`2^{B}` + * elements. When storing big objects, the size of these contiguous + * chunks can become too big, damaging performance. If this is + * measured to be problematic for a specific use-case, it can be + * solved by using a `immer::box` to wrap the type `T`. + * + * **Example** + * .. literalinclude:: ../example/set/intro.cpp + * :language: c++ + * :start-after: intro/start + * :end-before: intro/end + * + * @endrst + * + */ +template , + typename Equal = std::equal_to, + typename MemoryPolicy = default_memory_policy, + detail::hamts::bits_t B = default_bits> +class set +{ + using impl_t = detail::hamts::champ; + +public: + using key_type = T; + using value_type = T; + using size_type = detail::hamts::size_t; + using diference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = Equal; + using reference = const T&; + using const_reference = const T&; + + using iterator = detail::hamts::champ_iterator; + using const_iterator = iterator; + + using transient_type = set_transient; + + /*! + * Default constructor. It creates a set of `size() == 0`. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + set() = default; + + /*! + * Returns an iterator pointing at the first element of the + * collection. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + iterator begin() const { return {impl_}; } + + /*! + * Returns an iterator pointing just after the last element of the + * collection. It does not allocate and its complexity is @f$ O(1) @f$. + */ + iterator end() const { return {impl_, typename iterator::end_t{}}; } + + /*! + * Returns the number of elements in the container. It does + * not allocate memory and its complexity is @f$ O(1) @f$. + */ + size_type size() const { return impl_.size; } + + /*! + * Returns `1` when `value` is contained in the set or `0` + * otherwise. It won't allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + size_type count(const T& value) const + { return impl_.template get, + detail::constantly>(value); } + + /*! + * Returns whether the sets are equal. + */ + bool operator==(const set& other) const + { return impl_.equals(other.impl_); } + bool operator!=(const set& other) const + { return !(*this == other); } + + /*! + * Returns a set containing `value`. If the `value` is already in + * the set, it returns the same set. It may allocate memory and + * its complexity is *effectively* @f$ O(1) @f$. + */ + set insert(T value) const + { return impl_.add(std::move(value)); } + + /*! + * Returns a set without `value`. If the `value` is not in the + * set it returns the same set. It may allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + set erase(const T& value) const + { return impl_.sub(value); } + + /*! + * Returns an @a transient form of this container, a + * `immer::set_transient`. + */ + transient_type transient() const& + { return transient_type{ impl_ }; } + transient_type transient() && + { return transient_type{ std::move(impl_) }; } + + // Semi-private + const impl_t& impl() const { return impl_; } + +private: + friend transient_type; + + set(impl_t impl) + : impl_(std::move(impl)) + {} + + impl_t impl_ = impl_t::empty; +}; + +} // namespace immer diff --git a/src/immer/set_transient.hpp b/src/immer/set_transient.hpp new file mode 100644 index 000000000000..cba41601c694 --- /dev/null +++ b/src/immer/set_transient.hpp @@ -0,0 +1,28 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include + +#include + +namespace immer { + +/*! + * **WORK IN PROGRESS** + */ +template , + typename Equal = std::equal_to, + typename MemoryPolicy = default_memory_policy, + detail::hamts::bits_t B = default_bits> +class set_transient; + +} // namespace immer diff --git a/src/immer/transience/gc_transience_policy.hpp b/src/immer/transience/gc_transience_policy.hpp new file mode 100644 index 000000000000..7137bed83952 --- /dev/null +++ b/src/immer/transience/gc_transience_policy.hpp @@ -0,0 +1,110 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include + +#include +#include +#include + +namespace immer { + +/*! + * Provides transience ownership tracking when a *tracing garbage + * collector* is used instead of reference counting. + * + * @rst + * + * .. warning:: Using this policy without an allocation scheme that + * includes automatic tracing garbage collection may cause memory + * leaks. + * + * @endrst + */ +struct gc_transience_policy +{ + template + struct apply + { + struct type + { + using heap_ = typename HeapPolicy::type; + + struct edit + { + void* v; + edit() = delete; + bool operator==(edit x) const { return v == x.v; } + bool operator!=(edit x) const { return v != x.v; } + }; + + struct owner + { + void* make_token_() + { + return heap_::allocate(1, norefs_tag{}); + }; + + mutable std::atomic token_; + + operator edit () { return { token_ }; } + + owner() + : token_{make_token_()} + {} + owner(const owner& o) + : token_{make_token_()} + { + o.token_ = make_token_(); + } + owner(owner&& o) noexcept + : token_{o.token_.load()} + {} + owner& operator=(const owner& o) + { + o.token_ = make_token_(); + token_ = make_token_(); + return *this; + } + owner& operator=(owner&& o) noexcept + { + token_ = o.token_.load(); + return *this; + } + }; + + struct ownee + { + edit token_ {nullptr}; + + ownee& operator=(edit e) + { + assert(e != noone); + // This would be a nice safety plug but it sadly + // does not hold during transient concatenation. + // assert(token_ == e || token_ == edit{nullptr}); + token_ = e; + return *this; + } + + bool can_mutate(edit t) const { return token_ == t; } + bool owned() const { return token_ != edit{nullptr}; } + }; + + static owner noone; + }; + }; +}; + +template +typename gc_transience_policy::apply::type::owner +gc_transience_policy::apply::type::noone = {}; + +} // namespace immer diff --git a/src/immer/transience/no_transience_policy.hpp b/src/immer/transience/no_transience_policy.hpp new file mode 100644 index 000000000000..aa3d44ed4287 --- /dev/null +++ b/src/immer/transience/no_transience_policy.hpp @@ -0,0 +1,48 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +namespace immer { + +/*! + * Disables any special *transience* tracking. To be used when + * *reference counting* is available instead. + */ +struct no_transience_policy +{ + template + struct apply + { + struct type + { + struct edit {}; + + struct owner + { + operator edit () const { return {}; } + owner& operator=(const owner&) { return *this; }; + }; + + struct ownee + { + ownee& operator=(edit) { return *this; }; + bool can_mutate(edit) const { return false; } + bool owned() const { return false; } + }; + + static owner noone; + }; + }; +}; + +template +typename no_transience_policy::apply::type::owner +no_transience_policy::apply::type::noone = {}; + +} // namespace immer diff --git a/src/immer/vector.hpp b/src/immer/vector.hpp new file mode 100644 index 000000000000..f808a3242f70 --- /dev/null +++ b/src/immer/vector.hpp @@ -0,0 +1,352 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +#if IMMER_DEBUG_PRINT +#include +#endif + +namespace immer { + +template +class flex_vector; + +template +class vector_transient; + +/*! + * Immutable sequential container supporting both random access and + * structural sharing. + * + * @tparam T The type of the values to be stored in the container. + * @tparam MemoryPolicy Memory management policy. See @ref + * memory_policy. + * + * @rst + * + * This cotainer provides a good trade-off between cache locality, + * random access, update performance and structural sharing. It does + * so by storing the data in contiguous chunks of :math:`2^{BL}` + * elements. By default, when ``sizeof(T) == sizeof(void*)`` then + * :math:`B=BL=5`, such that data would be stored in contiguous + * chunks of :math:`32` elements. + * + * You may learn more about the meaning and implications of ``B`` and + * ``BL`` parameters in the :doc:`implementation` section. + * + * .. note:: In several methods we say that their complexity is + * *effectively* :math:`O(...)`. Do not confuse this with the word + * *amortized*, which has a very different meaning. In this + * context, *effective* means that while the + * mathematically rigurous + * complexity might be higher, for all practical matters the + * provided complexity is more useful to think about the actual + * cost of the operation. + * + * **Example** + * .. literalinclude:: ../example/vector/intro.cpp + * :language: c++ + * :start-after: intro/start + * :end-before: intro/end + * + * @endrst + */ +template > +class vector +{ + using impl_t = detail::rbts::rbtree; + using flex_t = flex_vector; + + using move_t = + std::integral_constant; + +public: + static constexpr auto bits = B; + static constexpr auto bits_leaf = BL; + using memory_policy = MemoryPolicy; + + using value_type = T; + using reference = const T&; + using size_type = detail::rbts::size_t; + using difference_type = std::ptrdiff_t; + using const_reference = const T&; + + using iterator = detail::rbts::rbtree_iterator; + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + + using transient_type = vector_transient; + + /*! + * Default constructor. It creates a vector of `size() == 0`. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + vector() = default; + + /*! + * Constructs a vector containing the elements in `values`. + */ + vector(std::initializer_list values) + : impl_{impl_t::from_initializer_list(values)} + {} + + /*! + * Constructs a vector containing the elements in the range + * defined by the input iterators `first` and `last`. + */ + template + vector(Iter first, Iter last) + : impl_{impl_t::from_range(first, last)} + {} + + /*! + * Constructs a vector containing the element `val` repeated `n` + * times. + */ + vector(size_type n, T v = {}) + : impl_{impl_t::from_fill(n, v)} + {} + + /*! + * Returns an iterator pointing at the first element of the + * collection. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + iterator begin() const { return {impl_}; } + + /*! + * Returns an iterator pointing just after the last element of the + * collection. It does not allocate and its complexity is @f$ O(1) @f$. + */ + iterator end() const { return {impl_, typename iterator::end_t{}}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing at the first element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing after the last element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rend() const { return reverse_iterator{begin()}; } + + /*! + * Returns the number of elements in the container. It does + * not allocate memory and its complexity is @f$ O(1) @f$. + */ + size_type size() const { return impl_.size; } + + /*! + * Returns `true` if there are no elements in the container. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + bool empty() const { return impl_.size == 0; } + + /*! + * Access the last element. + */ + const T& back() const { return impl_.back(); } + + /*! + * Access the first element. + */ + const T& front() const { return impl_.front(); } + + /*! + * Returns a `const` reference to the element at position `index`. + * It is undefined when @f$ 0 index \geq size() @f$. It does not + * allocate memory and its complexity is *effectively* @f$ O(1) + * @f$. + */ + reference operator[] (size_type index) const + { return impl_.get(index); } + + /*! + * Returns a `const` reference to the element at position + * `index`. It throws an `std::out_of_range` exception when @f$ + * index \geq size() @f$. It does not allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + reference at(size_type index) const + { return impl_.get_check(index); } + + /*! + * Returns whether the vectors are equal. + */ + bool operator==(const vector& other) const + { return impl_.equals(other.impl_); } + bool operator!=(const vector& other) const + { return !(*this == other); } + + /*! + * Returns a vector with `value` inserted at the end. It may + * allocate memory and its complexity is *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/vector/vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: push-back/start + * :end-before: push-back/end + * + * @endrst + */ + vector push_back(value_type value) const& + { return impl_.push_back(std::move(value)); } + + decltype(auto) push_back(value_type value) && + { return push_back_move(move_t{}, std::move(value)); } + + /*! + * Returns a vector containing value `value` at position `idx`. + * Undefined for `index >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/vector/vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: set/start + * :end-before: set/end + * + * @endrst + */ + vector set(size_type index, value_type value) const& + { return impl_.assoc(index, std::move(value)); } + + decltype(auto) set(size_type index, value_type value) && + { return set_move(move_t{}, index, std::move(value)); } + + /*! + * Returns a vector containing the result of the expression + * `fn((*this)[idx])` at position `idx`. + * Undefined for `0 >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/vector/vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: update/start + * :end-before: update/end + * + * @endrst + */ + template + vector update(size_type index, FnT&& fn) const& + { return impl_.update(index, std::forward(fn)); } + + template + decltype(auto) update(size_type index, FnT&& fn) && + { return update_move(move_t{}, index, std::forward(fn)); } + + /*! + * Returns a vector containing only the first `min(elems, size())` + * elements. It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + * + * @rst + * + * **Example** + * .. literalinclude:: ../example/vector/vector.cpp + * :language: c++ + * :dedent: 8 + * :start-after: take/start + * :end-before: take/end + * + * @endrst + */ + vector take(size_type elems) const& + { return impl_.take(elems); } + + decltype(auto) take(size_type elems) && + { return take_move(move_t{}, elems); } + + /*! + * Returns an @a transient form of this container, an + * `immer::vector_transient`. + */ + transient_type transient() const& + { return transient_type{ impl_ }; } + transient_type transient() && + { return transient_type{ std::move(impl_) }; } + + // Semi-private + const impl_t& impl() const { return impl_; } + +#if IMMER_DEBUG_PRINT + void debug_print() const + { flex_t{*this}.debug_print(); } +#endif + +private: + friend flex_t; + friend transient_type; + + vector(impl_t impl) + : impl_(std::move(impl)) + { +#if IMMER_DEBUG_PRINT + // force the compiler to generate debug_print, so we can call + // it from a debugger + [](volatile auto){}(&vector::debug_print); +#endif + } + + vector&& push_back_move(std::true_type, value_type value) + { impl_.push_back_mut({}, std::move(value)); return std::move(*this); } + vector push_back_move(std::false_type, value_type value) + { return impl_.push_back(std::move(value)); } + + vector&& set_move(std::true_type, size_type index, value_type value) + { impl_.assoc_mut({}, index, std::move(value)); return std::move(*this); } + vector set_move(std::false_type, size_type index, value_type value) + { return impl_.assoc(index, std::move(value)); } + + template + vector&& update_move(std::true_type, size_type index, Fn&& fn) + { impl_.update_mut({}, index, std::forward(fn)); return std::move(*this); } + template + vector update_move(std::false_type, size_type index, Fn&& fn) + { return impl_.update(index, std::forward(fn)); } + + vector&& take_move(std::true_type, size_type elems) + { impl_.take_mut({}, elems); return std::move(*this); } + vector take_move(std::false_type, size_type elems) + { return impl_.take(elems); } + + impl_t impl_ = impl_t::empty; +}; + +} // namespace immer diff --git a/src/immer/vector_transient.hpp b/src/immer/vector_transient.hpp new file mode 100644 index 000000000000..3d6f2fd6f1bc --- /dev/null +++ b/src/immer/vector_transient.hpp @@ -0,0 +1,189 @@ +// +// immer: immutable data structures for C++ +// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente +// +// This software is distributed under the Boost Software License, Version 1.0. +// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt +// + +#pragma once + +#include +#include +#include + +namespace immer { + +template +class vector; + +template +class flex_vector_transient; + +/*! + * Mutable version of `immer::vector`. + * + * @rst + * + * Refer to :doc:`transients` to learn more about when and how to use + * the mutable versions of immutable containers. + * + * @endrst + */ +template > +class vector_transient + : MemoryPolicy::transience_t::owner +{ + using impl_t = detail::rbts::rbtree; + using flex_t = flex_vector_transient; + using owner_t = typename MemoryPolicy::transience_t::owner; + +public: + static constexpr auto bits = B; + static constexpr auto bits_leaf = BL; + using memory_policy = MemoryPolicy; + + using value_type = T; + using reference = const T&; + using size_type = detail::rbts::size_t; + using difference_type = std::ptrdiff_t; + using const_reference = const T&; + + using iterator = detail::rbts::rbtree_iterator; + using const_iterator = iterator; + using reverse_iterator = std::reverse_iterator; + + using persistent_type = vector; + + /*! + * Default constructor. It creates a mutable vector of `size() == + * 0`. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + vector_transient() = default; + + /*! + * Returns an iterator pointing at the first element of the + * collection. It does not allocate memory and its complexity is + * @f$ O(1) @f$. + */ + iterator begin() const { return {impl_}; } + + /*! + * Returns an iterator pointing just after the last element of the + * collection. It does not allocate and its complexity is @f$ O(1) @f$. + */ + iterator end() const { return {impl_, typename iterator::end_t{}}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing at the first element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rbegin() const { return reverse_iterator{end()}; } + + /*! + * Returns an iterator that traverses the collection backwards, + * pointing after the last element of the reversed collection. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + reverse_iterator rend() const { return reverse_iterator{begin()}; } + + /*! + * Returns the number of elements in the container. It does + * not allocate memory and its complexity is @f$ O(1) @f$. + */ + size_type size() const { return impl_.size; } + + /*! + * Returns `true` if there are no elements in the container. It + * does not allocate memory and its complexity is @f$ O(1) @f$. + */ + bool empty() const { return impl_.size == 0; } + + /*! + * Returns a `const` reference to the element at position `index`. + * It is undefined when @f$ 0 index \geq size() @f$. It does not + * allocate memory and its complexity is *effectively* @f$ O(1) + * @f$. + */ + reference operator[] (size_type index) const + { return impl_.get(index); } + + /*! + * Returns a `const` reference to the element at position + * `index`. It throws an `std::out_of_range` exception when @f$ + * index \geq size() @f$. It does not allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + reference at(size_type index) const + { return impl_.get_check(index); } + + /*! + * Inserts `value` at the end. It may allocate memory and its + * complexity is *effectively* @f$ O(1) @f$. + */ + void push_back(value_type value) + { impl_.push_back_mut(*this, std::move(value)); } + + /*! + * Sets to the value `value` at position `idx`. + * Undefined for `index >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + void set(size_type index, value_type value) + { impl_.assoc_mut(*this, index, std::move(value)); } + + /*! + * Updates the vector to contain the result of the expression + * `fn((*this)[idx])` at position `idx`. + * Undefined for `0 >= size()`. + * It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + template + void update(size_type index, FnT&& fn) + { impl_.update_mut(*this, index, std::forward(fn)); } + + /*! + * Resizes the vector to only contain the first `min(elems, size())` + * elements. It may allocate memory and its complexity is + * *effectively* @f$ O(1) @f$. + */ + void take(size_type elems) + { impl_.take_mut(*this, elems); } + + /*! + * Returns an @a immutable form of this container, an + * `immer::vector`. + */ + persistent_type persistent() & + { + this->owner_t::operator=(owner_t{}); + return persistent_type{ impl_ }; + } + persistent_type persistent() && + { return persistent_type{ std::move(impl_) }; } + +private: + friend flex_t; + friend persistent_type; + + vector_transient(impl_t impl) + : impl_(std::move(impl)) + {} + + impl_t impl_ = impl_t::empty; +}; + +} // namespace immer diff --git a/src/init.cpp b/src/init.cpp index aac5d4d606db..48a8e0cc096d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -67,10 +67,14 @@ #include "spork.h" #include "warnings.h" +#include "evo/deterministicmns.h" + #include #include #include +#include "bls/bls.h" + #ifndef WIN32 #include #endif @@ -241,8 +245,13 @@ void PrepareShutdown() peerLogic.reset(); g_connman.reset(); - // STORE DATA CACHES INTO SERIALIZED DAT FILES if (!fLiteMode) { +#ifdef ENABLE_WALLET + // Stop PrivateSend, release keys + privateSendClient.fEnablePrivateSend = false; + privateSendClient.ResetPool(); +#endif + // STORE DATA CACHES INTO SERIALIZED DAT FILES CFlatDB flatdb1("mncache.dat", "magicMasternodeCache"); flatdb1.Dump(mnodeman); CFlatDB flatdb2("mnpayments.dat", "magicMasternodePaymentsCache"); @@ -251,6 +260,13 @@ void PrepareShutdown() flatdb3.Dump(governance); CFlatDB flatdb4("netfulfilled.dat", "magicFulfilledCache"); flatdb4.Dump(netfulfilledman); + if(fEnableInstantSend) + { + CFlatDB flatdb5("instantsend.dat", "magicInstantSendCache"); + flatdb5.Dump(instantsend); + } + CFlatDB flatdb6("sporks.dat", "magicSporkCache"); + flatdb6.Dump(sporkManager); } UnregisterNodeSignals(GetNodeSignals()); @@ -281,6 +297,10 @@ void PrepareShutdown() pcoinsdbview = NULL; delete pblocktree; pblocktree = NULL; + delete deterministicMNManager; + deterministicMNManager = NULL; + delete evoDb; + evoDb = NULL; } #ifdef ENABLE_WALLET if (pwalletMain) @@ -300,6 +320,9 @@ void PrepareShutdown() delete pdsNotificationInterface; pdsNotificationInterface = NULL; } + if (fMasternodeMode) { + UnregisterValidationInterface(activeMasternodeManager); + } #ifndef WIN32 try { @@ -485,9 +508,13 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-zmqpubhashblock=
", _("Enable publish hash block in
")); strUsage += HelpMessageOpt("-zmqpubhashtx=
", _("Enable publish hash transaction in
")); strUsage += HelpMessageOpt("-zmqpubhashtxlock=
", _("Enable publish hash transaction (locked via InstantSend) in
")); + strUsage += HelpMessageOpt("-zmqpubhashgovernancevote=
", _("Enable publish hash of governance votes in
")); + strUsage += HelpMessageOpt("-zmqpubhashgovernanceobject=
", _("Enable publish hash of governance objects (like proposals) in
")); + strUsage += HelpMessageOpt("-zmqpubhashinstantsenddoublespend=
", _("Enable publish transaction hashes of attempted InstantSend double spend in
")); strUsage += HelpMessageOpt("-zmqpubrawblock=
", _("Enable publish raw block in
")); strUsage += HelpMessageOpt("-zmqpubrawtx=
", _("Enable publish raw transaction in
")); strUsage += HelpMessageOpt("-zmqpubrawtxlock=
", _("Enable publish raw transaction (locked via InstantSend) in
")); + strUsage += HelpMessageOpt("-zmqpubrawinstantsenddoublespend=
", _("Enable publish raw transactions of attempted InstantSend double spend in
")); #endif strUsage += HelpMessageGroup(_("Debugging/Testing options:")); @@ -544,6 +571,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); AppendParamsHelpMessages(strUsage, showDebug); strUsage += HelpMessageOpt("-litemode=", strprintf(_("Disable all Absolute specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u)"), 0)); + strUsage += HelpMessageOpt("-sporkaddr=", strprintf(_("Override spork address. Only useful for regtest and povnet. Using this on mainnet or testnet will ban you."))); + strUsage += HelpMessageOpt("-minsporkkeys=", strprintf(_("Overrides minimum spork signers to change spork value. Only useful for regtest and povnet. Using this on mainnet or testnet will ban you."))); strUsage += HelpMessageGroup(_("Masternode options:")); strUsage += HelpMessageOpt("-masternode=", strprintf(_("Enable the client to act as a masternode (0-1, default: %u)"), 0)); @@ -555,6 +584,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("PrivateSend options:")); strUsage += HelpMessageOpt("-enableprivatesend=", strprintf(_("Enable use of automated PrivateSend for funds stored in this wallet (0-1, default: %u)"), 0)); strUsage += HelpMessageOpt("-privatesendmultisession=", strprintf(_("Enable multiple PrivateSend mixing sessions per block, experimental (0-1, default: %u)"), DEFAULT_PRIVATESEND_MULTISESSION)); + strUsage += HelpMessageOpt("-privatesendsessions=", strprintf(_("Use N separate masternodes in parallel to mix funds (%u-%u, default: %u)"), MIN_PRIVATESEND_SESSIONS, MAX_PRIVATESEND_SESSIONS, DEFAULT_PRIVATESEND_SESSIONS)); strUsage += HelpMessageOpt("-privatesendrounds=", strprintf(_("Use N separate masternodes for each denominated input to mix funds (%u-%u, default: %u)"), MIN_PRIVATESEND_ROUNDS, MAX_PRIVATESEND_ROUNDS, DEFAULT_PRIVATESEND_ROUNDS)); strUsage += HelpMessageOpt("-privatesendamount=", strprintf(_("Keep N ABS anonymized (%u-%u, default: %u)"), MIN_PRIVATESEND_AMOUNT, MAX_PRIVATESEND_AMOUNT, DEFAULT_PRIVATESEND_AMOUNT)); strUsage += HelpMessageOpt("-liquidityprovider=", strprintf(_("Provide liquidity to PrivateSend by infrequently mixing coins on a continual basis (%u-%u, default: %u, 1=very frequent, high fees, %u=very infrequent, low fees)"), @@ -563,7 +593,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("InstantSend options:")); strUsage += HelpMessageOpt("-enableinstantsend=", strprintf(_("Enable InstantSend, show confirmations for locked transactions (0-1, default: %u)"), 1)); - strUsage += HelpMessageOpt("-instantsenddepth=", strprintf(_("Show N confirmations for a successfully locked transaction (%u-%u, default: %u)"), MIN_INSTANTSEND_DEPTH, MAX_INSTANTSEND_DEPTH, DEFAULT_INSTANTSEND_DEPTH)); strUsage += HelpMessageOpt("-instantsendnotify=", _("Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID)")); @@ -576,7 +605,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Minimum bytes per sigop in transactions we relay and mine (default: %u)"), DEFAULT_BYTES_PER_SIGOP)); strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER)); strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); - strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT)); strUsage += HelpMessageGroup(_("Block creation options:")); strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); @@ -771,6 +799,15 @@ void ThreadImport(std::vector vImportFiles) StartShutdown(); } } // End scope of CImportingNow + + // force UpdatedBlockTip to initialize nCachedBlockHeight for DS, MN payments and budgets + // but don't call it directly to prevent triggering of other listeners like zmq etc. + // GetMainSignals().UpdatedBlockTip(chainActive.Tip()); + pdsNotificationInterface->InitializeCurrentBlockTip(); + + if (activeMasternodeManager && fAIP0003ActiveAtTip) + activeMasternodeManager->Init(); + LoadMempool(); fDumpMempoolLater = !fRequestShutdown; } @@ -893,6 +930,8 @@ void InitParameterInteraction() if (nLiqProvTmp > 0) { ForceSetArg("-enableprivatesend", "1"); LogPrintf("%s: parameter interaction: -liquidityprovider=%d -> setting -enableprivatesend=1\n", __func__, nLiqProvTmp); + ForceSetArg("-privatesendsessions", itostr(MIN_PRIVATESEND_SESSIONS)); + LogPrintf("%s: parameter interaction: -liquidityprovider=%d -> setting -privatesendsessions=%d\n", __func__, nLiqProvTmp, itostr(std::numeric_limits::max())); ForceSetArg("-privatesendrounds", itostr(std::numeric_limits::max())); LogPrintf("%s: parameter interaction: -liquidityprovider=%d -> setting -privatesendrounds=%d\n", __func__, nLiqProvTmp, itostr(std::numeric_limits::max())); ForceSetArg("-privatesendamount", itostr(MAX_PRIVATESEND_AMOUNT)); @@ -1218,15 +1257,6 @@ bool AppInitParameterInteraction() nMaxTipAge = GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); - fEnableReplacement = GetBoolArg("-mempoolreplacement", DEFAULT_ENABLE_REPLACEMENT); - if ((!fEnableReplacement) && IsArgSet("-mempoolreplacement")) { - // Minimal effort at forwards compatibility - std::string strReplacementModeList = GetArg("-mempoolreplacement", ""); // default is impossible - std::vector vstrReplacementModes; - boost::split(vstrReplacementModes, strReplacementModeList, boost::is_any_of(",")); - fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end()); - } - if (mapMultiArgs.count("-bip9params")) { // Allow overriding BIP9 parameters for testing if (!chainparams.MineBlocksOnDemand()) { @@ -1343,10 +1373,28 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) threadGroup.create_thread(&ThreadScriptCheck); } - if (IsArgSet("-sporkkey")) // spork priv key - { - if (!sporkManager.SetPrivKey(GetArg("-sporkkey", ""))) + std::vector vSporkAddresses; + if (mapMultiArgs.count("-sporkaddr")) { + vSporkAddresses = mapMultiArgs.at("-sporkaddr"); + } else { + vSporkAddresses = Params().SporkAddresses(); + } + for (const auto& address: vSporkAddresses) { + if (!sporkManager.SetSporkAddress(address)) { + return InitError(_("Invalid spork address specified with -sporkaddr")); + } + } + + int minsporkkeys = GetArg("-minsporkkeys", Params().MinSporkKeys()); + if (!sporkManager.SetMinSporkKeys(minsporkkeys)) { + return InitError(_("Invalid minimum number of spork signers specified with -minsporkkeys")); + } + + + if (IsArgSet("-sporkkey")) { // spork priv key + if (!sporkManager.SetPrivKey(GetArg("-sporkkey", ""))) { return InitError(_("Unable to sign spork message, wrong key?")); + } } // Start the lightweight task scheduler thread @@ -1501,7 +1549,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) if (!mapMultiArgs.count("-bind") && !mapMultiArgs.count("-whitebind")) { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; - fBound |= Bind(connman, CService(in6addr_any, GetListenPort()), BF_NONE); + fBound |= Bind(connman, CService((in6_addr)IN6ADDR_ANY_INIT, GetListenPort()), BF_NONE); fBound |= Bind(connman, CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); } if (!fBound) @@ -1585,6 +1633,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) nTotalCache -= nCoinDBCache; nCoinCacheUsage = nTotalCache; // the rest goes to in-memory cache int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; + int64_t nEvoDbCache = 1024 * 1024 * 16; // TODO LogPrintf("Cache configuration:\n"); LogPrintf("* Using %.1fMiB for block index database\n", nBlockTreeDBCache * (1.0 / 1024 / 1024)); LogPrintf("* Using %.1fMiB for chain state database\n", nCoinDBCache * (1.0 / 1024 / 1024)); @@ -1607,11 +1656,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) delete pcoinsdbview; delete pcoinscatcher; delete pblocktree; + delete deterministicMNManager; + delete evoDb; + + evoDb = new CEvoDB(nEvoDbCache, false, fReindex || fReindexChainState); pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState); pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinscatcher); + deterministicMNManager = new CDeterministicMNManager(*evoDb); if (fReindex) { pblocktree->WriteReindexing(true); @@ -1659,6 +1713,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) break; } + // Needs to be called after chain is initialized + if (chainActive.Tip() && chainActive.Tip()->pprev) { + fAIP0003ActiveAtTip = VersionBitsState(chainActive.Tip()->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_AIP0003, versionbitscache) == THRESHOLD_ACTIVE; + } + uiInterface.InitMessage(_("Verifying blocks...")); if (fHavePruned && GetArg("-checkblocks", DEFAULT_CHECKBLOCKS) > MIN_BLOCKS_TO_KEEP) { LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks", @@ -1790,11 +1849,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) fLiteMode = GetBoolArg("-litemode", false); if(fLiteMode) { - InitWarning(_("You are starting in lite mode, all Dash-specific functionality is disabled.")); + InitWarning(_("You are starting in lite mode, all Absolute-specific functionality is disabled.")); } if((!fLiteMode && fTxIndex == false) - && chainparams.NetworkIDString() != CBaseChainParams::REGTEST) { // TODO remove this when pruning is fixed. See https://github.com/dashpay/dash/pull/1817 and https://github.com/dashpay/dash/pull/1743 + && chainparams.NetworkIDString() != CBaseChainParams::REGTEST) { return InitError(_("Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index.")); } @@ -1807,13 +1866,43 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) std::string strMasterNodePrivKey = GetArg("-masternodeprivkey", ""); if(!strMasterNodePrivKey.empty()) { - if(!CMessageSigner::GetKeysFromSecret(strMasterNodePrivKey, activeMasternode.keyMasternode, activeMasternode.pubKeyMasternode)) + CPubKey pubKeyMasternode; + if(!CMessageSigner::GetKeysFromSecret(strMasterNodePrivKey, activeMasternodeInfo.legacyKeyOperator, pubKeyMasternode)) return InitError(_("Invalid masternodeprivkey. Please see documenation.")); - LogPrintf(" pubKeyMasternode: %s\n", CBitcoinAddress(activeMasternode.pubKeyMasternode.GetID()).ToString()); + activeMasternodeInfo.legacyKeyIDOperator = pubKeyMasternode.GetID(); + + LogPrintf(" keyIDOperator: %s\n", CBitcoinAddress(activeMasternodeInfo.legacyKeyIDOperator).ToString()); } else { return InitError(_("You must specify a masternodeprivkey in the configuration. Please see documentation for help.")); } + + std::string strMasterNodeBLSPrivKey = GetArg("-masternodeblsprivkey", ""); + if(!strMasterNodeBLSPrivKey.empty()) { + auto binKey = ParseHex(strMasterNodeBLSPrivKey); + CBLSSecretKey keyOperator; + keyOperator.SetBuf(binKey); + if (keyOperator.IsValid()) { + activeMasternodeInfo.blsKeyOperator = std::make_unique(keyOperator); + activeMasternodeInfo.blsPubKeyOperator = std::make_unique(activeMasternodeInfo.blsKeyOperator->GetPublicKey()); + LogPrintf(" blsPubKeyOperator: %s\n", keyOperator.GetPublicKey().ToString()); + } else { + return InitError(_("Invalid masternodeblsprivkey. Please see documenation.")); + } + } else { + InitWarning(_("You should specify a masternodeblsprivkey in the configuration. Please see documentation for help.")); + } + + // init and register activeMasternodeManager + activeMasternodeManager = new CActiveDeterministicMasternodeManager(); + RegisterValidationInterface(activeMasternodeManager); + } + + if (activeMasternodeInfo.blsKeyOperator == nullptr) { + activeMasternodeInfo.blsKeyOperator = std::make_unique(); + } + if (activeMasternodeInfo.blsPubKeyOperator == nullptr) { + activeMasternodeInfo.blsPubKeyOperator = std::make_unique(); } #ifdef ENABLE_WALLET @@ -1848,16 +1937,14 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) privateSendClient.fEnablePrivateSend = GetBoolArg("-enableprivatesend", false); privateSendClient.fPrivateSendMultiSession = GetBoolArg("-privatesendmultisession", DEFAULT_PRIVATESEND_MULTISESSION); + privateSendClient.nPrivateSendSessions = std::min(std::max((int)GetArg("-privatesendsessions", DEFAULT_PRIVATESEND_SESSIONS), MIN_PRIVATESEND_SESSIONS), MAX_PRIVATESEND_SESSIONS); privateSendClient.nPrivateSendRounds = std::min(std::max((int)GetArg("-privatesendrounds", DEFAULT_PRIVATESEND_ROUNDS), MIN_PRIVATESEND_ROUNDS), nMaxRounds); privateSendClient.nPrivateSendAmount = std::min(std::max((int)GetArg("-privatesendamount", DEFAULT_PRIVATESEND_AMOUNT), MIN_PRIVATESEND_AMOUNT), MAX_PRIVATESEND_AMOUNT); #endif // ENABLE_WALLET fEnableInstantSend = GetBoolArg("-enableinstantsend", 1); - nInstantSendDepth = GetArg("-instantsenddepth", DEFAULT_INSTANTSEND_DEPTH); - nInstantSendDepth = std::min(std::max(nInstantSendDepth, MIN_INSTANTSEND_DEPTH), MAX_INSTANTSEND_DEPTH); LogPrintf("fLiteMode %d\n", fLiteMode); - LogPrintf("nInstantSendDepth %d\n", nInstantSendDepth); #ifdef ENABLE_WALLET LogPrintf("PrivateSend liquidityprovider: %d\n", privateSendClient.nLiquidityProvider); LogPrintf("PrivateSend rounds: %d\n", privateSendClient.nPrivateSendRounds); @@ -1906,25 +1993,45 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) if(!flatdb4.Load(netfulfilledman)) { return InitError(_("Failed to load fulfilled requests cache from") + "\n" + (pathDB / strDBName).string()); } + + if(fEnableInstantSend) + { + strDBName = "instantsend.dat"; + uiInterface.InitMessage(_("Loading InstantSend data cache...")); + CFlatDB flatdb5(strDBName, "magicInstantSendCache"); + if(!flatdb5.Load(instantsend)) { + return InitError(_("Failed to load InstantSend data cache from") + "\n" + (pathDB / strDBName).string()); + } + } + + strDBName = "sporks.dat"; + uiInterface.InitMessage(_("Loading sporks cache...")); + CFlatDB flatdb6(strDBName, "magicSporkCache"); + if(!flatdb6.Load(sporkManager)) { + return InitError(_("Failed to load sporks cache from") + "\n" + (pathDB / strDBName).string()); + } } + // ********************************************************* Step 11c: schedule Dash-specific tasks - // ********************************************************* Step 11c: update block tip in Absolute modules + if (!fLiteMode) { + scheduler.scheduleEvery(boost::bind(&CNetFulfilledRequestManager::DoMaintenance, boost::ref(netfulfilledman)), 60); + scheduler.scheduleEvery(boost::bind(&CMasternodeSync::DoMaintenance, boost::ref(masternodeSync), boost::ref(*g_connman)), MASTERNODE_SYNC_TICK_SECONDS); + scheduler.scheduleEvery(boost::bind(&CMasternodeMan::DoMaintenance, boost::ref(mnodeman), boost::ref(*g_connman)), 1); + scheduler.scheduleEvery(boost::bind(&CActiveLegacyMasternodeManager::DoMaintenance, boost::ref(legacyActiveMasternodeManager), boost::ref(*g_connman)), MASTERNODE_MIN_MNP_SECONDS); - // force UpdatedBlockTip to initialize nCachedBlockHeight for DS, MN payments and budgets - // but don't call it directly to prevent triggering of other listeners like zmq etc. - // GetMainSignals().UpdatedBlockTip(chainActive.Tip()); - pdsNotificationInterface->InitializeCurrentBlockTip(); + scheduler.scheduleEvery(boost::bind(&CMasternodePayments::DoMaintenance, boost::ref(mnpayments)), 60); + scheduler.scheduleEvery(boost::bind(&CGovernanceManager::DoMaintenance, boost::ref(governance), boost::ref(*g_connman)), 60 * 5); - // ********************************************************* Step 11d: start absolute-ps- threads + scheduler.scheduleEvery(boost::bind(&CInstantSend::DoMaintenance, boost::ref(instantsend)), 60); - threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSend, boost::ref(*g_connman))); - if (fMasternodeMode) - threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSendServer, boost::ref(*g_connman))); + if (fMasternodeMode) + scheduler.scheduleEvery(boost::bind(&CPrivateSendServer::DoMaintenance, boost::ref(privateSendServer), boost::ref(*g_connman)), 1); #ifdef ENABLE_WALLET - else - threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSendClient, boost::ref(*g_connman))); + else + scheduler.scheduleEvery(boost::bind(&CPrivateSendClientManager::DoMaintenance, boost::ref(privateSendClient), boost::ref(*g_connman)), 1); #endif // ENABLE_WALLET + } // ********************************************************* Step 12: start node diff --git a/src/instantx.cpp b/src/instantx.cpp index 26ff7017771e..acffec62c177 100644 --- a/src/instantx.cpp +++ b/src/instantx.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -34,10 +34,13 @@ extern CWallet* pwalletMain; extern CTxMemPool mempool; bool fEnableInstantSend = true; -int nInstantSendDepth = DEFAULT_INSTANTSEND_DEPTH; int nCompleteTXLocks; +std::atomic CInstantSend::isAutoLockBip9Active{false}; +const double CInstantSend::AUTO_IX_MEMPOOL_THRESHOLD = 0.1; + CInstantSend instantsend; +const std::string CInstantSend::SERIALIZATION_VERSION_STRING = "CInstantSend-Version-1"; // Transaction Locks // @@ -52,13 +55,12 @@ CInstantSend instantsend; void CInstantSend::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { - if(fLiteMode) return; // disable all Absolute specific functionality - if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; + if (fLiteMode) return; // disable all Absolute specific functionality + if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; // NOTE: NetMsgType::TXLOCKREQUEST is handled via ProcessMessage() in net_processing.cpp - if (strCommand == NetMsgType::TXLOCKVOTE) // InstantSend Transaction Lock Consensus Votes - { + if (strCommand == NetMsgType::TXLOCKVOTE) { // InstantSend Transaction Lock Consensus Votes if(pfrom->nVersion < MIN_INSTANTSEND_PROTO_VERSION) { LogPrint("instantsend", "TXLOCKVOTE -- peer=%d using obsolete version %i\n", pfrom->id, pfrom->nVersion); connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, @@ -75,7 +77,7 @@ void CInstantSend::ProcessMessage(CNode* pfrom, const std::string& strCommand, C pfrom->setAskFor.erase(nVoteHash); // Ignore any InstantSend messages until masternode list is synced - if(!masternodeSync.IsMasternodeListSynced()) return; + if (!masternodeSync.IsMasternodeListSynced()) return; { LOCK(cs_instantsend); @@ -102,7 +104,7 @@ bool CInstantSend::ProcessTxLockRequest(const CTxLockRequest& txLockRequest, CCo // Check to see if we conflict with existing completed lock for (const auto& txin : txLockRequest.tx->vin) { std::map::iterator it = mapLockedOutpoints.find(txin.prevout); - if(it != mapLockedOutpoints.end() && it->second != txLockRequest.GetHash()) { + if (it != mapLockedOutpoints.end() && it->second != txLockRequest.GetHash()) { // Conflicting with complete lock, proceed to see if we should cancel them both LogPrintf("CInstantSend::ProcessTxLockRequest -- WARNING: Found conflicting completed Transaction Lock, txid=%s, completed lock txid=%s\n", txLockRequest.GetHash().ToString(), it->second.ToString()); @@ -113,18 +115,24 @@ bool CInstantSend::ProcessTxLockRequest(const CTxLockRequest& txLockRequest, CCo // if so - do not fail, just warn user for (const auto& txin : txLockRequest.tx->vin) { std::map >::iterator it = mapVotedOutpoints.find(txin.prevout); - if(it != mapVotedOutpoints.end()) { + if (it != mapVotedOutpoints.end()) { for (const auto& hash : it->second) { - if(hash != txLockRequest.GetHash()) { + if (hash != txLockRequest.GetHash()) { LogPrint("instantsend", "CInstantSend::ProcessTxLockRequest -- Double spend attempt! %s\n", txin.prevout.ToStringShort()); // do not fail here, let it go and see which one will get the votes to be locked - // TODO: notify zmq+script + // NOTIFY ZMQ + CTransaction txCurrent = *txLockRequest.tx; // currently processed tx + auto itPrevious = mapTxLockCandidates.find(hash); + if (itPrevious != mapTxLockCandidates.end() && itPrevious->second.txLockRequest) { + CTransaction txPrevious = *itPrevious->second.txLockRequest.tx; // previously locked one + GetMainSignals().NotifyInstantSendDoubleSpendAttempt(txCurrent, txPrevious); + } } } } } - if(!CreateTxLockCandidate(txLockRequest)) { + if (!CreateTxLockCandidate(txLockRequest)) { // smth is not right LogPrintf("CInstantSend::ProcessTxLockRequest -- CreateTxLockCandidate failed, txid=%s\n", txHash.ToString()); return false; @@ -143,19 +151,19 @@ bool CInstantSend::ProcessTxLockRequest(const CTxLockRequest& txLockRequest, CCo bool CInstantSend::CreateTxLockCandidate(const CTxLockRequest& txLockRequest) { - if(!txLockRequest.IsValid()) return false; + if (!txLockRequest.IsValid()) return false; LOCK(cs_instantsend); uint256 txHash = txLockRequest.GetHash(); std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); - if(itLockCandidate == mapTxLockCandidates.end()) { + if (itLockCandidate == mapTxLockCandidates.end()) { LogPrintf("CInstantSend::CreateTxLockCandidate -- new, txid=%s\n", txHash.ToString()); CTxLockCandidate txLockCandidate(txLockRequest); // all inputs should already be checked by txLockRequest.IsValid() above, just use them now - for(const auto& txin : txLockRequest.tx->vin) { + for (const auto& txin : txLockRequest.tx->vin) { txLockCandidate.AddOutPointLock(txin.prevout); } mapTxLockCandidates.insert(std::make_pair(txHash, txLockCandidate)); @@ -169,7 +177,7 @@ bool CInstantSend::CreateTxLockCandidate(const CTxLockRequest& txLockRequest) LogPrintf("CInstantSend::CreateTxLockCandidate -- update empty, txid=%s\n", txHash.ToString()); // all inputs should already be checked by txLockRequest.IsValid() above, just use them now - for(const auto& txin : txLockRequest.tx->vin) { + for (const auto& txin : txLockRequest.tx->vin) { itLockCandidate->second.AddOutPointLock(txin.prevout); } } else { @@ -212,23 +220,21 @@ void CInstantSend::Vote(const uint256& txHash, CConnman& connman) void CInstantSend::Vote(CTxLockCandidate& txLockCandidate, CConnman& connman) { - if(!fMasternodeMode) return; - if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; + if (!fMasternodeMode) return; + if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; AssertLockHeld(cs_main); AssertLockHeld(cs_instantsend); uint256 txHash = txLockCandidate.GetHash(); // We should never vote on a Transaction Lock Request that was not (yet) accepted by the mempool - if(mapLockRequestAccepted.find(txHash) == mapLockRequestAccepted.end()) return; + if (mapLockRequestAccepted.find(txHash) == mapLockRequestAccepted.end()) return; // check if we need to vote on this candidate's outpoints, // it's possible that we need to vote for several of them - std::map::iterator itOutpointLock = txLockCandidate.mapOutPointLocks.begin(); - while(itOutpointLock != txLockCandidate.mapOutPointLocks.end()) { - - int nPrevoutHeight = GetUTXOHeight(itOutpointLock->first); - if(nPrevoutHeight == -1) { - LogPrint("instantsend", "CInstantSend::Vote -- Failed to find UTXO %s\n", itOutpointLock->first.ToStringShort()); + for (auto& outpointLockPair : txLockCandidate.mapOutPointLocks) { + int nPrevoutHeight = GetUTXOHeight(outpointLockPair.first); + if (nPrevoutHeight == -1) { + LogPrint("instantsend", "CInstantSend::Vote -- Failed to find UTXO %s\n", outpointLockPair.first.ToStringShort()); return; } @@ -236,52 +242,49 @@ void CInstantSend::Vote(CTxLockCandidate& txLockCandidate, CConnman& connman) int nRank; int nMinRequiredProtocol = std::max(MIN_INSTANTSEND_PROTO_VERSION, mnpayments.GetMinMasternodePaymentsProto()); - if(!mnodeman.GetMasternodeRank(activeMasternode.outpoint, nRank, nLockInputHeight, nMinRequiredProtocol)) { - LogPrint("instantsend", "CInstantSend::Vote -- Can't calculate rank for masternode %s\n", activeMasternode.outpoint.ToStringShort()); - ++itOutpointLock; + if (!mnodeman.GetMasternodeRank(activeMasternodeInfo.outpoint, nRank, nLockInputHeight, nMinRequiredProtocol)) { + LogPrint("instantsend", "CInstantSend::Vote -- Can't calculate rank for masternode %s\n", activeMasternodeInfo.outpoint.ToStringShort()); continue; } int nSignaturesTotal = COutPointLock::SIGNATURES_TOTAL; - if(nRank > nSignaturesTotal) { + if (nRank > nSignaturesTotal) { LogPrint("instantsend", "CInstantSend::Vote -- Masternode not in the top %d (%d)\n", nSignaturesTotal, nRank); - ++itOutpointLock; continue; } LogPrint("instantsend", "CInstantSend::Vote -- In the top %d (%d)\n", nSignaturesTotal, nRank); - std::map >::iterator itVoted = mapVotedOutpoints.find(itOutpointLock->first); + std::map >::iterator itVoted = mapVotedOutpoints.find(outpointLockPair.first); // Check to see if we already voted for this outpoint, // refuse to vote twice or to include the same outpoint in another tx bool fAlreadyVoted = false; - if(itVoted != mapVotedOutpoints.end()) { + if (itVoted != mapVotedOutpoints.end()) { for (const auto& hash : itVoted->second) { std::map::iterator it2 = mapTxLockCandidates.find(hash); - if(it2->second.HasMasternodeVoted(itOutpointLock->first, activeMasternode.outpoint)) { + if (it2->second.HasMasternodeVoted(outpointLockPair.first, activeMasternodeInfo.outpoint)) { // we already voted for this outpoint to be included either in the same tx or in a competing one, // skip it anyway fAlreadyVoted = true; LogPrintf("CInstantSend::Vote -- WARNING: We already voted for this outpoint, skipping: txHash=%s, outpoint=%s\n", - txHash.ToString(), itOutpointLock->first.ToStringShort()); + txHash.ToString(), outpointLockPair.first.ToStringShort()); break; } } } - if(fAlreadyVoted) { - ++itOutpointLock; + if (fAlreadyVoted) { continue; // skip to the next outpoint } // we haven't voted for this outpoint yet, let's try to do this now - CTxLockVote vote(txHash, itOutpointLock->first, activeMasternode.outpoint); + CTxLockVote vote(txHash, outpointLockPair.first, activeMasternodeInfo.outpoint); - if(!vote.Sign()) { + if (!vote.Sign()) { LogPrintf("CInstantSend::Vote -- Failed to sign consensus vote\n"); return; } - if(!vote.CheckSignature()) { + if (!vote.CheckSignature()) { LogPrintf("CInstantSend::Vote -- Signature invalid\n"); return; } @@ -289,27 +292,25 @@ void CInstantSend::Vote(CTxLockCandidate& txLockCandidate, CConnman& connman) // vote constructed sucessfully, let's store and relay it uint256 nVoteHash = vote.GetHash(); mapTxLockVotes.insert(std::make_pair(nVoteHash, vote)); - if(itOutpointLock->second.AddVote(vote)) { + if (outpointLockPair.second.AddVote(vote)) { LogPrintf("CInstantSend::Vote -- Vote created successfully, relaying: txHash=%s, outpoint=%s, vote=%s\n", - txHash.ToString(), itOutpointLock->first.ToStringShort(), nVoteHash.ToString()); + txHash.ToString(), outpointLockPair.first.ToStringShort(), nVoteHash.ToString()); - if(itVoted == mapVotedOutpoints.end()) { + if (itVoted == mapVotedOutpoints.end()) { std::set setHashes; setHashes.insert(txHash); - mapVotedOutpoints.insert(std::make_pair(itOutpointLock->first, setHashes)); + mapVotedOutpoints.insert(std::make_pair(outpointLockPair.first, setHashes)); } else { - mapVotedOutpoints[itOutpointLock->first].insert(txHash); - if(mapVotedOutpoints[itOutpointLock->first].size() > 1) { + mapVotedOutpoints[outpointLockPair.first].insert(txHash); + if (mapVotedOutpoints[outpointLockPair.first].size() > 1) { // it's ok to continue, just warn user LogPrintf("CInstantSend::Vote -- WARNING: Vote conflicts with some existing votes: txHash=%s, outpoint=%s, vote=%s\n", - txHash.ToString(), itOutpointLock->first.ToStringShort(), nVoteHash.ToString()); + txHash.ToString(), outpointLockPair.first.ToStringShort(), nVoteHash.ToString()); } } vote.Relay(connman); } - - ++itOutpointLock; } } @@ -318,7 +319,7 @@ bool CInstantSend::ProcessNewTxLockVote(CNode* pfrom, const CTxLockVote& vote, C uint256 txHash = vote.GetTxHash(); uint256 nVoteHash = vote.GetHash(); - if(!vote.IsValid(pfrom, connman)) { + if (!vote.IsValid(pfrom, connman)) { // could be because of missing MN LogPrint("instantsend", "CInstantSend::%s -- Vote is invalid, txid=%s\n", __func__, txHash.ToString()); return false; @@ -337,9 +338,9 @@ bool CInstantSend::ProcessNewTxLockVote(CNode* pfrom, const CTxLockVote& vote, C // will actually process only after the lock request itself has arrived std::map::iterator it = mapTxLockCandidates.find(txHash); - if(it == mapTxLockCandidates.end() || !it->second.txLockRequest) { + if (it == mapTxLockCandidates.end() || !it->second.txLockRequest) { // no or empty tx lock candidate - if(it == mapTxLockCandidates.end()) { + if (it == mapTxLockCandidates.end()) { // start timeout countdown after the very first vote CreateEmptyTxLockCandidate(txHash); } @@ -352,10 +353,10 @@ bool CInstantSend::ProcessNewTxLockVote(CNode* pfrom, const CTxLockVote& vote, C int nMasternodeOrphanExpireTime = GetTime() + 60*10; // keep time data for 10 minutes auto itMnOV = mapMasternodeOrphanVotes.find(vote.GetMasternodeOutpoint()); - if(itMnOV == mapMasternodeOrphanVotes.end()) { + if (itMnOV == mapMasternodeOrphanVotes.end()) { mapMasternodeOrphanVotes.emplace(vote.GetMasternodeOutpoint(), nMasternodeOrphanExpireTime); } else { - if(itMnOV->second > GetTime() && itMnOV->second > GetAverageMasternodeOrphanVoteTime()) { + if (itMnOV->second > GetTime() && itMnOV->second > GetAverageMasternodeOrphanVoteTime()) { LogPrint("instantsend", "CInstantSend::%s -- masternode is spamming orphan Transaction Lock Votes: txid=%s masternode=%s\n", __func__, txHash.ToString(), vote.GetMasternodeOutpoint().ToStringShort()); // Misbehaving(pfrom->id, 1); @@ -380,7 +381,7 @@ bool CInstantSend::ProcessNewTxLockVote(CNode* pfrom, const CTxLockVote& vote, C UpdateVotedOutpoints(vote, txLockCandidate); - if(!txLockCandidate.AddVote(vote)) { + if (!txLockCandidate.AddVote(vote)) { // this should never happen return false; } @@ -409,7 +410,7 @@ bool CInstantSend::ProcessOrphanTxLockVote(const CTxLockVote& vote) // We shouldn't process orphan votes without a valid tx lock candidate std::map::iterator it = mapTxLockCandidates.find(txHash); - if(it == mapTxLockCandidates.end() || !it->second.txLockRequest) + if (it == mapTxLockCandidates.end() || !it->second.txLockRequest) return false; // this shouldn never happen CTxLockCandidate& txLockCandidate = it->second; @@ -423,7 +424,7 @@ bool CInstantSend::ProcessOrphanTxLockVote(const CTxLockVote& vote) UpdateVotedOutpoints(vote, txLockCandidate); - if(!txLockCandidate.AddVote(vote)) { + if (!txLockCandidate.AddVote(vote)) { // this should never happen return false; } @@ -443,14 +444,14 @@ void CInstantSend::UpdateVotedOutpoints(const CTxLockVote& vote, CTxLockCandidat uint256 txHash = vote.GetTxHash(); std::map >::iterator it1 = mapVotedOutpoints.find(vote.GetOutpoint()); - if(it1 != mapVotedOutpoints.end()) { + if (it1 != mapVotedOutpoints.end()) { for (const auto& hash : it1->second) { - if(hash != txHash) { + if (hash != txHash) { // same outpoint was already voted to be locked by another tx lock request, // let's see if it was the same masternode who voted on this outpoint // for another tx lock request std::map::iterator it2 = mapTxLockCandidates.find(hash); - if(it2 !=mapTxLockCandidates.end() && it2->second.HasMasternodeVoted(vote.GetOutpoint(), vote.GetMasternodeOutpoint())) { + if (it2 !=mapTxLockCandidates.end() && it2->second.HasMasternodeVoted(vote.GetOutpoint(), vote.GetMasternodeOutpoint())) { // yes, it was the same masternode LogPrintf("CInstantSend::%s -- masternode sent conflicting votes! %s\n", __func__, vote.GetMasternodeOutpoint().ToStringShort()); // mark both Lock Candidates as attacked, none of them should complete, @@ -479,8 +480,8 @@ void CInstantSend::ProcessOrphanTxLockVotes() AssertLockHeld(cs_instantsend); std::map::iterator it = mapTxLockVotesOrphan.begin(); - while(it != mapTxLockVotesOrphan.end()) { - if(ProcessOrphanTxLockVote(it->second)) { + while (it != mapTxLockVotesOrphan.end()) { + if (ProcessOrphanTxLockVote(it->second)) { mapTxLockVotesOrphan.erase(it++); } else { ++it; @@ -490,16 +491,16 @@ void CInstantSend::ProcessOrphanTxLockVotes() void CInstantSend::TryToFinalizeLockCandidate(const CTxLockCandidate& txLockCandidate) { - if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; + if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; AssertLockHeld(cs_main); AssertLockHeld(cs_instantsend); uint256 txHash = txLockCandidate.txLockRequest.tx->GetHash(); - if(txLockCandidate.IsAllOutPointsReady() && !IsLockedInstantSendTransaction(txHash)) { + if (txLockCandidate.IsAllOutPointsReady() && !IsLockedInstantSendTransaction(txHash)) { // we have enough votes now LogPrint("instantsend", "CInstantSend::TryToFinalizeLockCandidate -- Transaction Lock is ready to complete, txid=%s\n", txHash.ToString()); - if(ResolveConflicts(txLockCandidate)) { + if (ResolveConflicts(txLockCandidate)) { LockTransactionInputs(txLockCandidate); UpdateLockedTransaction(txLockCandidate); } @@ -511,22 +512,23 @@ void CInstantSend::UpdateLockedTransaction(const CTxLockCandidate& txLockCandida // cs_main, cs_wallet and cs_instantsend should be already locked AssertLockHeld(cs_main); #ifdef ENABLE_WALLET - if (pwalletMain) + if (pwalletMain) { AssertLockHeld(pwalletMain->cs_wallet); + } #endif AssertLockHeld(cs_instantsend); uint256 txHash = txLockCandidate.GetHash(); - if(!IsLockedInstantSendTransaction(txHash)) return; // not a locked tx, do not update/notify + if (!IsLockedInstantSendTransaction(txHash)) return; // not a locked tx, do not update/notify #ifdef ENABLE_WALLET - if(pwalletMain && pwalletMain->UpdatedTransaction(txHash)) { + if (pwalletMain && pwalletMain->UpdatedTransaction(txHash)) { // bumping this to update UI nCompleteTXLocks++; // notify an external script once threshold is reached std::string strCmd = GetArg("-instantsendnotify", ""); - if(!strCmd.empty()) { + if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", txHash.GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } @@ -540,19 +542,16 @@ void CInstantSend::UpdateLockedTransaction(const CTxLockCandidate& txLockCandida void CInstantSend::LockTransactionInputs(const CTxLockCandidate& txLockCandidate) { - if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; + if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; LOCK(cs_instantsend); uint256 txHash = txLockCandidate.GetHash(); - if(!txLockCandidate.IsAllOutPointsReady()) return; - - std::map::const_iterator it = txLockCandidate.mapOutPointLocks.begin(); + if (!txLockCandidate.IsAllOutPointsReady()) return; - while(it != txLockCandidate.mapOutPointLocks.end()) { - mapLockedOutpoints.insert(std::make_pair(it->first, txHash)); - ++it; + for (const auto& pair : txLockCandidate.mapOutPointLocks) { + mapLockedOutpoints.insert(std::make_pair(pair.first, txHash)); } LogPrint("instantsend", "CInstantSend::LockTransactionInputs -- done, txid=%s\n", txHash.ToString()); } @@ -561,7 +560,7 @@ bool CInstantSend::GetLockedOutPointTxHash(const COutPoint& outpoint, uint256& h { LOCK(cs_instantsend); std::map::iterator it = mapLockedOutpoints.find(outpoint); - if(it == mapLockedOutpoints.end()) return false; + if (it == mapLockedOutpoints.end()) return false; hashRet = it->second; return true; } @@ -574,18 +573,18 @@ bool CInstantSend::ResolveConflicts(const CTxLockCandidate& txLockCandidate) uint256 txHash = txLockCandidate.GetHash(); // make sure the lock is ready - if(!txLockCandidate.IsAllOutPointsReady()) return false; + if (!txLockCandidate.IsAllOutPointsReady()) return false; AssertLockHeld(mempool.cs); // protect mempool.mapNextTx for (const auto& txin : txLockCandidate.txLockRequest.tx->vin) { uint256 hashConflicting; - if(GetLockedOutPointTxHash(txin.prevout, hashConflicting) && txHash != hashConflicting) { + if (GetLockedOutPointTxHash(txin.prevout, hashConflicting) && txHash != hashConflicting) { // completed lock which conflicts with another completed one? // this means that majority of MNs in the quorum for this specific tx input are malicious! std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); std::map::iterator itLockCandidateConflicting = mapTxLockCandidates.find(hashConflicting); - if(itLockCandidate == mapTxLockCandidates.end() || itLockCandidateConflicting == mapTxLockCandidates.end()) { + if (itLockCandidate == mapTxLockCandidates.end() || itLockCandidateConflicting == mapTxLockCandidates.end()) { // safety check, should never really happen LogPrintf("CInstantSend::ResolveConflicts -- ERROR: Found conflicting completed Transaction Lock, but one of txLockCandidate-s is missing, txid=%s, conflicting txid=%s\n", txHash.ToString(), hashConflicting.ToString()); @@ -615,7 +614,7 @@ bool CInstantSend::ResolveConflicts(const CTxLockCandidate& txLockCandidate) } else if (mempool.mapNextTx.count(txin.prevout)) { // check if it's in mempool hashConflicting = mempool.mapNextTx.find(txin.prevout)->second->GetHash(); - if(txHash == hashConflicting) continue; // matches current, not a conflict, skip to next txin + if (txHash == hashConflicting) continue; // matches current, not a conflict, skip to next txin // conflicts with tx in mempool LogPrintf("CInstantSend::ResolveConflicts -- ERROR: Failed to complete Transaction Lock, conflicts with mempool, txid=%s\n", txHash.ToString()); return false; @@ -624,14 +623,14 @@ bool CInstantSend::ResolveConflicts(const CTxLockCandidate& txLockCandidate) // No conflicts were found so far, check to see if it was already included in block CTransactionRef txTmp; uint256 hashBlock; - if(GetTransaction(txHash, txTmp, Params().GetConsensus(), hashBlock, true) && hashBlock != uint256()) { + if (GetTransaction(txHash, txTmp, Params().GetConsensus(), hashBlock, true) && hashBlock != uint256()) { LogPrint("instantsend", "CInstantSend::ResolveConflicts -- Done, %s is included in block %s\n", txHash.ToString(), hashBlock.ToString()); return true; } // Not in block yet, make sure all its inputs are still unspent for (const auto& txin : txLockCandidate.txLockRequest.tx->vin) { Coin coin; - if(!GetUTXOCoin(txin.prevout, coin)) { + if (!GetUTXOCoin(txin.prevout, coin)) { // Not in UTXO anymore? A conflicting tx was mined while we were waiting for votes. LogPrintf("CInstantSend::ResolveConflicts -- ERROR: Failed to find UTXO %s, can't complete Transaction Lock\n", txin.prevout.ToStringShort()); return false; @@ -646,14 +645,11 @@ int64_t CInstantSend::GetAverageMasternodeOrphanVoteTime() { LOCK(cs_instantsend); // NOTE: should never actually call this function when mapMasternodeOrphanVotes is empty - if(mapMasternodeOrphanVotes.empty()) return 0; + if (mapMasternodeOrphanVotes.empty()) return 0; - std::map::iterator it = mapMasternodeOrphanVotes.begin(); int64_t total = 0; - - while(it != mapMasternodeOrphanVotes.end()) { - total+= it->second; - ++it; + for (const auto& pair : mapMasternodeOrphanVotes) { + total += pair.second; } return total / mapMasternodeOrphanVotes.size(); @@ -661,23 +657,22 @@ int64_t CInstantSend::GetAverageMasternodeOrphanVoteTime() void CInstantSend::CheckAndRemove() { - if(!masternodeSync.IsMasternodeListSynced()) return; + if (!masternodeSync.IsMasternodeListSynced()) return; LOCK(cs_instantsend); std::map::iterator itLockCandidate = mapTxLockCandidates.begin(); // remove expired candidates - while(itLockCandidate != mapTxLockCandidates.end()) { + while (itLockCandidate != mapTxLockCandidates.end()) { CTxLockCandidate &txLockCandidate = itLockCandidate->second; uint256 txHash = txLockCandidate.GetHash(); - if(txLockCandidate.IsExpired(nCachedBlockHeight)) { + if (txLockCandidate.IsExpired(nCachedBlockHeight)) { LogPrintf("CInstantSend::CheckAndRemove -- Removing expired Transaction Lock Candidate: txid=%s\n", txHash.ToString()); - std::map::iterator itOutpointLock = txLockCandidate.mapOutPointLocks.begin(); - while(itOutpointLock != txLockCandidate.mapOutPointLocks.end()) { - mapLockedOutpoints.erase(itOutpointLock->first); - mapVotedOutpoints.erase(itOutpointLock->first); - ++itOutpointLock; + + for (const auto& pair : txLockCandidate.mapOutPointLocks) { + mapLockedOutpoints.erase(pair.first); + mapVotedOutpoints.erase(pair.first); } mapLockRequestAccepted.erase(txHash); mapLockRequestRejected.erase(txHash); @@ -689,8 +684,8 @@ void CInstantSend::CheckAndRemove() // remove expired votes std::map::iterator itVote = mapTxLockVotes.begin(); - while(itVote != mapTxLockVotes.end()) { - if(itVote->second.IsExpired(nCachedBlockHeight)) { + while (itVote != mapTxLockVotes.end()) { + if (itVote->second.IsExpired(nCachedBlockHeight)) { LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing expired vote: txid=%s masternode=%s\n", itVote->second.GetTxHash().ToString(), itVote->second.GetMasternodeOutpoint().ToStringShort()); mapTxLockVotes.erase(itVote++); @@ -701,8 +696,8 @@ void CInstantSend::CheckAndRemove() // remove timed out orphan votes std::map::iterator itOrphanVote = mapTxLockVotesOrphan.begin(); - while(itOrphanVote != mapTxLockVotesOrphan.end()) { - if(itOrphanVote->second.IsTimedOut()) { + while (itOrphanVote != mapTxLockVotesOrphan.end()) { + if (itOrphanVote->second.IsTimedOut()) { LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing timed out orphan vote: txid=%s masternode=%s\n", itOrphanVote->second.GetTxHash().ToString(), itOrphanVote->second.GetMasternodeOutpoint().ToStringShort()); mapTxLockVotes.erase(itOrphanVote->first); @@ -714,8 +709,8 @@ void CInstantSend::CheckAndRemove() // remove invalid votes and votes for failed lock attempts itVote = mapTxLockVotes.begin(); - while(itVote != mapTxLockVotes.end()) { - if(itVote->second.IsFailed()) { + while (itVote != mapTxLockVotes.end()) { + if (itVote->second.IsFailed()) { LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing vote for failed lock attempt: txid=%s masternode=%s\n", itVote->second.GetTxHash().ToString(), itVote->second.GetMasternodeOutpoint().ToStringShort()); mapTxLockVotes.erase(itVote++); @@ -726,8 +721,8 @@ void CInstantSend::CheckAndRemove() // remove timed out masternode orphan votes (DOS protection) std::map::iterator itMasternodeOrphan = mapMasternodeOrphanVotes.begin(); - while(itMasternodeOrphan != mapMasternodeOrphanVotes.end()) { - if(itMasternodeOrphan->second < GetTime()) { + while (itMasternodeOrphan != mapMasternodeOrphanVotes.end()) { + if (itMasternodeOrphan->second < GetTime()) { LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing timed out orphan masternode vote: masternode=%s\n", itMasternodeOrphan->first.ToStringShort()); mapMasternodeOrphanVotes.erase(itMasternodeOrphan++); @@ -769,7 +764,7 @@ bool CInstantSend::GetTxLockRequest(const uint256& txHash, CTxLockRequest& txLoc LOCK(cs_instantsend); std::map::iterator it = mapTxLockCandidates.find(txHash); - if(it == mapTxLockCandidates.end() || !it->second.txLockRequest) return false; + if (it == mapTxLockCandidates.end() || !it->second.txLockRequest) return false; txLockRequestRet = it->second.txLockRequest; return true; @@ -780,44 +775,45 @@ bool CInstantSend::GetTxLockVote(const uint256& hash, CTxLockVote& txLockVoteRet LOCK(cs_instantsend); std::map::iterator it = mapTxLockVotes.find(hash); - if(it == mapTxLockVotes.end()) return false; + if (it == mapTxLockVotes.end()) return false; txLockVoteRet = it->second; return true; } -bool CInstantSend::IsInstantSendReadyToLock(const uint256& txHash) +void CInstantSend::Clear() { - if(!fEnableInstantSend || GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound() || - !sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return false; - LOCK(cs_instantsend); - // There must be a successfully verified lock request - // and all outputs must be locked (i.e. have enough signatures) - std::map::iterator it = mapTxLockCandidates.find(txHash); - return it != mapTxLockCandidates.end() && it->second.IsAllOutPointsReady(); + + mapLockRequestAccepted.clear(); + mapLockRequestRejected.clear(); + mapTxLockVotes.clear(); + mapTxLockVotesOrphan.clear(); + mapTxLockCandidates.clear(); + mapVotedOutpoints.clear(); + mapLockedOutpoints.clear(); + mapMasternodeOrphanVotes.clear(); + nCachedBlockHeight = 0; } bool CInstantSend::IsLockedInstantSendTransaction(const uint256& txHash) { - if(!fEnableInstantSend || GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound() || + if (!fEnableInstantSend || GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound() || !sporkManager.IsSporkActive(SPORK_3_INSTANTSEND_BLOCK_FILTERING)) return false; LOCK(cs_instantsend); // there must be a lock candidate std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); - if(itLockCandidate == mapTxLockCandidates.end()) return false; + if (itLockCandidate == mapTxLockCandidates.end()) return false; // which should have outpoints - if(itLockCandidate->second.mapOutPointLocks.empty()) return false; + if (itLockCandidate->second.mapOutPointLocks.empty()) return false; // and all of these outputs must be included in mapLockedOutpoints with correct hash - std::map::iterator itOutpointLock = itLockCandidate->second.mapOutPointLocks.begin(); - while(itOutpointLock != itLockCandidate->second.mapOutPointLocks.end()) { + for (const auto& pair : itLockCandidate->second.mapOutPointLocks) { uint256 hashLocked; - if(!GetLockedOutPointTxHash(itOutpointLock->first, hashLocked) || hashLocked != txHash) return false; - ++itOutpointLock; + if (!GetLockedOutPointTxHash(pair.first, hashLocked) || hashLocked != txHash) return false; } return true; @@ -825,28 +821,23 @@ bool CInstantSend::IsLockedInstantSendTransaction(const uint256& txHash) int CInstantSend::GetTransactionLockSignatures(const uint256& txHash) { - if(!fEnableInstantSend) return -1; - if(GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound()) return -2; - if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return -3; + if (!fEnableInstantSend) return -1; + if (GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound()) return -2; + if (!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return -3; LOCK(cs_instantsend); std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); - if(itLockCandidate != mapTxLockCandidates.end()) { + if (itLockCandidate != mapTxLockCandidates.end()) { return itLockCandidate->second.CountVotes(); } return -1; } -int CInstantSend::GetConfirmations(const uint256 &nTXHash) -{ - return IsLockedInstantSendTransaction(nTXHash) ? nInstantSendDepth : 0; -} - bool CInstantSend::IsTxLockCandidateTimedOut(const uint256& txHash) { - if(!fEnableInstantSend) return false; + if (!fEnableInstantSend) return false; LOCK(cs_instantsend); @@ -892,63 +883,66 @@ void CInstantSend::SyncTransaction(const CTransaction& tx, const CBlockIndex *pi // Check lock candidates std::map::iterator itLockCandidate = mapTxLockCandidates.find(txHash); - if(itLockCandidate != mapTxLockCandidates.end()) { + if (itLockCandidate != mapTxLockCandidates.end()) { LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d lock candidate updated\n", txHash.ToString(), nHeightNew); itLockCandidate->second.SetConfirmedHeight(nHeightNew); // Loop through outpoint locks - std::map::iterator itOutpointLock = itLockCandidate->second.mapOutPointLocks.begin(); - while(itOutpointLock != itLockCandidate->second.mapOutPointLocks.end()) { + for (const auto& pair : itLockCandidate->second.mapOutPointLocks) { // Check corresponding lock votes - std::vector vVotes = itOutpointLock->second.GetVotes(); - std::vector::iterator itVote = vVotes.begin(); - std::map::iterator it; - while(itVote != vVotes.end()) { - uint256 nVoteHash = itVote->GetHash(); + for (const auto& vote : pair.second.GetVotes()) { + uint256 nVoteHash = vote.GetHash(); LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated\n", txHash.ToString(), nHeightNew, nVoteHash.ToString()); - it = mapTxLockVotes.find(nVoteHash); - if(it != mapTxLockVotes.end()) { + const auto& it = mapTxLockVotes.find(nVoteHash); + if (it != mapTxLockVotes.end()) { it->second.SetConfirmedHeight(nHeightNew); } - ++itVote; } - ++itOutpointLock; } } // check orphan votes - std::map::iterator itOrphanVote = mapTxLockVotesOrphan.begin(); - while(itOrphanVote != mapTxLockVotesOrphan.end()) { - if(itOrphanVote->second.GetTxHash() == txHash) { + for (const auto& pair : mapTxLockVotesOrphan) { + if (pair.second.GetTxHash() == txHash) { LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated\n", - txHash.ToString(), nHeightNew, itOrphanVote->first.ToString()); - mapTxLockVotes[itOrphanVote->first].SetConfirmedHeight(nHeightNew); + txHash.ToString(), nHeightNew, pair.first.ToString()); + mapTxLockVotes[pair.first].SetConfirmedHeight(nHeightNew); } - ++itOrphanVote; } } -std::string CInstantSend::ToString() +std::string CInstantSend::ToString() const { LOCK(cs_instantsend); return strprintf("Lock Candidates: %llu, Votes %llu", mapTxLockCandidates.size(), mapTxLockVotes.size()); } +bool CInstantSend::CanAutoLock() +{ + if (!isAutoLockBip9Active) { + return false; + } + if (!sporkManager.IsSporkActive(SPORK_16_INSTANTSEND_AUTOLOCKS)) { + return false; + } + return (mempool.UsedMemoryShare() < AUTO_IX_MEMPOOL_THRESHOLD); +} + // // CTxLockRequest // bool CTxLockRequest::IsValid() const { - if(tx->vout.size() < 1) return false; + if (tx->vout.size() < 1) return false; - if(tx->vin.size() > WARN_MANY_INPUTS) { + if (tx->vin.size() > WARN_MANY_INPUTS) { LogPrint("instantsend", "CTxLockRequest::IsValid -- WARNING: Too many inputs: tx=%s", ToString()); } AssertLockHeld(cs_main); - if(!CheckFinalTx(*tx)) { + if (!CheckFinalTx(*tx)) { LogPrint("instantsend", "CTxLockRequest::IsValid -- Transaction is not final: tx=%s", ToString()); return false; } @@ -961,7 +955,7 @@ bool CTxLockRequest::IsValid() const Coin coin; - if(!GetUTXOCoin(txin.prevout, coin)) { + if (!GetUTXOCoin(txin.prevout, coin)) { LogPrint("instantsend", "CTxLockRequest::IsValid -- Failed to find UTXO %s\n", txin.prevout.ToStringShort()); return false; } @@ -970,7 +964,7 @@ bool CTxLockRequest::IsValid() const // 1 less than the "send IX" gui requires, in case of a block propagating the network at the time int nConfirmationsRequired = nInstantSendConfirmationsRequired - 1; - if(nTxAge < nConfirmationsRequired) { + if (nTxAge < nConfirmationsRequired) { LogPrint("instantsend", "CTxLockRequest::IsValid -- outpoint %s too new: nTxAge=%d, nConfirmationsRequired=%d, txid=%s\n", txin.prevout.ToStringShort(), nTxAge, nConfirmationsRequired, GetHash().ToString()); return false; @@ -979,14 +973,14 @@ bool CTxLockRequest::IsValid() const nValueIn += coin.out.nValue; } - if(nValueIn > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { + if (nValueIn > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { LogPrint("instantsend", "CTxLockRequest::IsValid -- Transaction value too high: nValueIn=%d, tx=%s", nValueIn, ToString()); return false; } CAmount nValueOut = tx->GetValueOut(); - if(nValueIn - nValueOut < GetMinFee()) { + if (nValueIn - nValueOut < GetMinFee(false)) { LogPrint("instantsend", "CTxLockRequest::IsValid -- did not include enough fees in transaction: fees=%d, tx=%s", nValueOut - nValueIn, ToString()); return false; } @@ -994,8 +988,11 @@ bool CTxLockRequest::IsValid() const return true; } -CAmount CTxLockRequest::GetMinFee() const +CAmount CTxLockRequest::GetMinFee(bool fForceMinFee) const { + if (!fForceMinFee && CInstantSend::CanAutoLock() && IsSimple()) { + return CAmount(); + } CAmount nMinFee = MIN_FEE; return std::max(nMinFee, CAmount(tx->vin.size() * nMinFee)); } @@ -1005,20 +1002,25 @@ int CTxLockRequest::GetMaxSignatures() const return tx->vin.size() * COutPointLock::SIGNATURES_TOTAL; } +bool CTxLockRequest::IsSimple() const +{ + return (tx->vin.size() <= MAX_INPUTS_FOR_AUTO_IX); +} + // // CTxLockVote // bool CTxLockVote::IsValid(CNode* pnode, CConnman& connman) const { - if(!mnodeman.Has(outpointMasternode)) { + if (!mnodeman.Has(outpointMasternode)) { LogPrint("instantsend", "CTxLockVote::IsValid -- Unknown masternode %s\n", outpointMasternode.ToStringShort()); mnodeman.AskForMN(pnode, outpointMasternode, connman); return false; } Coin coin; - if(!GetUTXOCoin(outpoint, coin)) { + if (!GetUTXOCoin(outpoint, coin)) { LogPrint("instantsend", "CTxLockVote::IsValid -- Failed to find UTXO %s\n", outpoint.ToStringShort()); return false; } @@ -1027,7 +1029,7 @@ bool CTxLockVote::IsValid(CNode* pnode, CConnman& connman) const int nRank; int nMinRequiredProtocol = std::max(MIN_INSTANTSEND_PROTO_VERSION, mnpayments.GetMinMasternodePaymentsProto()); - if(!mnodeman.GetMasternodeRank(outpointMasternode, nRank, nLockInputHeight, nMinRequiredProtocol)) { + if (!mnodeman.GetMasternodeRank(outpointMasternode, nRank, nLockInputHeight, nMinRequiredProtocol)) { //can be caused by past versions trying to vote with an invalid protocol LogPrint("instantsend", "CTxLockVote::IsValid -- Can't calculate rank for masternode %s\n", outpointMasternode.ToStringShort()); return false; @@ -1035,13 +1037,13 @@ bool CTxLockVote::IsValid(CNode* pnode, CConnman& connman) const LogPrint("instantsend", "CTxLockVote::IsValid -- Masternode %s, rank=%d\n", outpointMasternode.ToStringShort(), nRank); int nSignaturesTotal = COutPointLock::SIGNATURES_TOTAL; - if(nRank > nSignaturesTotal) { + if (nRank > nSignaturesTotal) { LogPrint("instantsend", "CTxLockVote::IsValid -- Masternode %s is not in the top %d (%d), vote hash=%s\n", outpointMasternode.ToStringShort(), nSignaturesTotal, nRank, GetHash().ToString()); return false; } - if(!CheckSignature()) { + if (!CheckSignature()) { LogPrintf("CTxLockVote::IsValid -- Signature invalid\n"); return false; } @@ -1065,18 +1067,27 @@ bool CTxLockVote::CheckSignature() const masternode_info_t infoMn; - if(!mnodeman.GetMasternodeInfo(outpointMasternode, infoMn)) { + if (!mnodeman.GetMasternodeInfo(outpointMasternode, infoMn)) { LogPrintf("CTxLockVote::CheckSignature -- Unknown Masternode: masternode=%s\n", outpointMasternode.ToString()); return false; } - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, infoMn.pubKeyMasternode, vchMasternodeSignature, strError)) { + CBLSSignature sig; + sig.SetBuf(vchMasternodeSignature); + if (!sig.IsValid() || !sig.VerifyInsecure(infoMn.blsPubKeyOperator, hash)) { + LogPrintf("CTxLockVote::CheckSignature -- VerifyInsecure() failed\n"); + return false; + } + } else if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + uint256 hash = GetSignatureHash(); + + if (!CHashSigner::VerifyHash(hash, infoMn.legacyKeyIDOperator, vchMasternodeSignature, strError)) { // could be a signature in old format std::string strMessage = txHash.ToString() + outpoint.ToStringShort(); - if(!CMessageSigner::VerifyMessage(infoMn.pubKeyMasternode, vchMasternodeSignature, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(infoMn.legacyKeyIDOperator, vchMasternodeSignature, strMessage, strError)) { // nope, not in old format either LogPrintf("CTxLockVote::CheckSignature -- VerifyMessage() failed, error: %s\n", strError); return false; @@ -1084,7 +1095,7 @@ bool CTxLockVote::CheckSignature() const } } else { std::string strMessage = txHash.ToString() + outpoint.ToStringShort(); - if(!CMessageSigner::VerifyMessage(infoMn.pubKeyMasternode, vchMasternodeSignature, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(infoMn.legacyKeyIDOperator, vchMasternodeSignature, strMessage, strError)) { LogPrintf("CTxLockVote::CheckSignature -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -1097,27 +1108,32 @@ bool CTxLockVote::Sign() { std::string strError; - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + uint256 hash = GetSignatureHash(); + + CBLSSignature sig = activeMasternodeInfo.blsKeyOperator->Sign(hash); + sig.GetBuf(vchMasternodeSignature); + } else if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if(!CHashSigner::SignHash(hash, activeMasternode.keyMasternode, vchMasternodeSignature)) { + if (!CHashSigner::SignHash(hash, activeMasternodeInfo.legacyKeyOperator, vchMasternodeSignature)) { LogPrintf("CTxLockVote::Sign -- SignHash() failed\n"); return false; } - if (!CHashSigner::VerifyHash(hash, activeMasternode.pubKeyMasternode, vchMasternodeSignature, strError)) { + if (!CHashSigner::VerifyHash(hash, activeMasternodeInfo.legacyKeyIDOperator, vchMasternodeSignature, strError)) { LogPrintf("CTxLockVote::Sign -- VerifyHash() failed, error: %s\n", strError); return false; } } else { std::string strMessage = txHash.ToString() + outpoint.ToStringShort(); - if(!CMessageSigner::SignMessage(strMessage, vchMasternodeSignature, activeMasternode.keyMasternode)) { + if (!CMessageSigner::SignMessage(strMessage, vchMasternodeSignature, activeMasternodeInfo.legacyKeyOperator)) { LogPrintf("CTxLockVote::Sign -- SignMessage() failed\n"); return false; } - if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, vchMasternodeSignature, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(activeMasternodeInfo.legacyKeyIDOperator, vchMasternodeSignature, strMessage, strError)) { LogPrintf("CTxLockVote::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -1129,7 +1145,12 @@ bool CTxLockVote::Sign() void CTxLockVote::Relay(CConnman& connman) const { CInv inv(MSG_TXLOCK_VOTE, GetHash()); - connman.RelayInv(inv); + CTxLockRequest request; + if (instantsend.GetTxLockRequest(txHash, request)) { + connman.RelayInvFiltered(inv, *request.tx); + } else { + connman.RelayInv(inv); + } } bool CTxLockVote::IsExpired(int nHeight) const @@ -1154,19 +1175,14 @@ bool CTxLockVote::IsFailed() const bool COutPointLock::AddVote(const CTxLockVote& vote) { - if(mapMasternodeVotes.count(vote.GetMasternodeOutpoint())) - return false; - mapMasternodeVotes.insert(std::make_pair(vote.GetMasternodeOutpoint(), vote)); - return true; + return mapMasternodeVotes.emplace(vote.GetMasternodeOutpoint(), vote).second; } std::vector COutPointLock::GetVotes() const { std::vector vRet; - std::map::const_iterator itVote = mapMasternodeVotes.begin(); - while(itVote != mapMasternodeVotes.end()) { - vRet.push_back(itVote->second); - ++itVote; + for (const auto& pair : mapMasternodeVotes) { + vRet.push_back(pair.second); } return vRet; } @@ -1178,10 +1194,8 @@ bool COutPointLock::HasMasternodeVoted(const COutPoint& outpointMasternodeIn) co void COutPointLock::Relay(CConnman& connman) const { - std::map::const_iterator itVote = mapMasternodeVotes.begin(); - while(itVote != mapMasternodeVotes.end()) { - itVote->second.Relay(connman); - ++itVote; + for (const auto& pair : mapMasternodeVotes) { + pair.second.Relay(connman); } } @@ -1197,25 +1211,23 @@ void CTxLockCandidate::AddOutPointLock(const COutPoint& outpoint) void CTxLockCandidate::MarkOutpointAsAttacked(const COutPoint& outpoint) { std::map::iterator it = mapOutPointLocks.find(outpoint); - if(it != mapOutPointLocks.end()) + if (it != mapOutPointLocks.end()) it->second.MarkAsAttacked(); } bool CTxLockCandidate::AddVote(const CTxLockVote& vote) { std::map::iterator it = mapOutPointLocks.find(vote.GetOutpoint()); - if(it == mapOutPointLocks.end()) return false; + if (it == mapOutPointLocks.end()) return false; return it->second.AddVote(vote); } bool CTxLockCandidate::IsAllOutPointsReady() const { - if(mapOutPointLocks.empty()) return false; + if (mapOutPointLocks.empty()) return false; - std::map::const_iterator it = mapOutPointLocks.begin(); - while(it != mapOutPointLocks.end()) { - if(!it->second.IsReady()) return false; - ++it; + for (const auto& pair : mapOutPointLocks) { + if (!pair.second.IsReady()) return false; } return true; } @@ -1230,10 +1242,8 @@ int CTxLockCandidate::CountVotes() const { // Note: do NOT use vote count to figure out if tx is locked, use IsAllOutPointsReady() instead int nCountVotes = 0; - std::map::const_iterator it = mapOutPointLocks.begin(); - while(it != mapOutPointLocks.end()) { - nCountVotes += it->second.CountVotes(); - ++it; + for (const auto& pair : mapOutPointLocks) { + nCountVotes += pair.second.CountVotes(); } return nCountVotes; } @@ -1252,9 +1262,7 @@ bool CTxLockCandidate::IsTimedOut() const void CTxLockCandidate::Relay(CConnman& connman) const { connman.RelayTransaction(*txLockRequest.tx); - std::map::const_iterator itOutpointLock = mapOutPointLocks.begin(); - while(itOutpointLock != mapOutPointLocks.end()) { - itOutpointLock->second.Relay(connman); - ++itOutpointLock; + for (const auto& pair : mapOutPointLocks) { + pair.second.Relay(connman); } } diff --git a/src/instantx.h b/src/instantx.h index 694c957690c8..90624fc6808b 100644 --- a/src/instantx.h +++ b/src/instantx.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef INSTANTX_H @@ -27,15 +27,9 @@ extern CInstantSend instantsend; (1000/2900.0)**5 = 0.004875397277841433 */ -// The INSTANTSEND_DEPTH is the "pseudo block depth" level assigned to locked -// txs to indicate the degree of confidence in their eventual confirmation and -// inability to be double-spent (adjustable via command line argument) -static const int MIN_INSTANTSEND_DEPTH = 0; -static const int MAX_INSTANTSEND_DEPTH = 60; -/// Default number of "pseudo-confirmations" for an InstantSend tx -static const int DEFAULT_INSTANTSEND_DEPTH = 5; +static const int MIN_INSTANTSEND_PROTO_VERSION = 70211; -static const int MIN_INSTANTSEND_PROTO_VERSION = 70209; +static const int MIN_INSTANTSEND_WITHOUT_FEE_PROTO_VERSION = 70211; /// For how long we are going to accept votes/locks /// after we saw the first one for a specific transaction @@ -45,7 +39,6 @@ static const int INSTANTSEND_LOCK_TIMEOUT_SECONDS = 15; static const int INSTANTSEND_FAILED_TIMEOUT_SECONDS = 60; extern bool fEnableInstantSend; -extern int nInstantSendDepth; extern int nCompleteTXLocks; /** @@ -54,6 +47,11 @@ extern int nCompleteTXLocks; class CInstantSend { private: + static const std::string SERIALIZATION_VERSION_STRING; + /// Automatic locks of "simple" transactions are only allowed + /// when mempool usage is lower than this threshold + static const double AUTO_IX_MEMPOOL_THRESHOLD; + // Keep track of current block height int nCachedBlockHeight; @@ -89,10 +87,38 @@ class CInstantSend void UpdateLockedTransaction(const CTxLockCandidate& txLockCandidate); bool ResolveConflicts(const CTxLockCandidate& txLockCandidate); - bool IsInstantSendReadyToLock(const uint256 &txHash); - public: - CCriticalSection cs_instantsend; + mutable CCriticalSection cs_instantsend; + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + std::string strVersion; + if(ser_action.ForRead()) { + READWRITE(strVersion); + } + else { + strVersion = SERIALIZATION_VERSION_STRING; + READWRITE(strVersion); + } + + READWRITE(mapLockRequestAccepted); + READWRITE(mapLockRequestRejected); + READWRITE(mapTxLockVotes); + READWRITE(mapTxLockVotesOrphan); + READWRITE(mapTxLockCandidates); + READWRITE(mapVotedOutpoints); + READWRITE(mapLockedOutpoints); + READWRITE(mapMasternodeOrphanVotes); + READWRITE(nCachedBlockHeight); + + if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { + Clear(); + } + } + + void Clear(); void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); @@ -114,8 +140,6 @@ class CInstantSend bool IsLockedInstantSendTransaction(const uint256& txHash); /// Get the actual number of accepted lock signatures int GetTransactionLockSignatures(const uint256& txHash); - /// Get instantsend confirmations (only) - int GetConfirmations(const uint256 &nTXHash); /// Remove expired entries from maps void CheckAndRemove(); @@ -127,7 +151,15 @@ class CInstantSend void UpdatedBlockTip(const CBlockIndex *pindex); void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); - std::string ToString(); + std::string ToString() const; + + void DoMaintenance() { CheckAndRemove(); } + + /// checks if we can automatically lock "simple" transactions + static bool CanAutoLock(); + + /// flag of the AutoLock Bip9 activation + static std::atomic isAutoLockBip9Active; }; /** @@ -137,6 +169,9 @@ class CTxLockRequest { private: static const CAmount MIN_FEE = 0.0001 * COIN; + /// If transaction has less or equal inputs than MAX_INPUTS_FOR_AUTO_IX, + /// it will be automatically locked + static const int MAX_INPUTS_FOR_AUTO_IX = 4; public: /// Warn for a large number of inputs to an IS tx - fees could be substantial @@ -147,6 +182,7 @@ class CTxLockRequest CTxLockRequest() : tx(MakeTransactionRef()) {} CTxLockRequest(const CTransaction& _tx) : tx(MakeTransactionRef(_tx)) {}; + CTxLockRequest(const CTransactionRef& _tx) : tx(_tx) {}; ADD_SERIALIZE_METHODS; @@ -156,9 +192,12 @@ class CTxLockRequest } bool IsValid() const; - CAmount GetMinFee() const; + CAmount GetMinFee(bool fForceMinFee) const; int GetMaxSignatures() const; + // checks if related transaction is "simple" to lock it automatically + bool IsSimple() const; + const uint256 &GetHash() const { return tx->GetHash(); } @@ -265,6 +304,8 @@ class COutPointLock static const int SIGNATURES_REQUIRED = 6; static const int SIGNATURES_TOTAL = 10; + COutPointLock() {} + COutPointLock(const COutPoint& outpointIn) : outpoint(outpointIn), mapMasternodeVotes() @@ -272,6 +313,15 @@ class COutPointLock COutPoint GetOutpoint() const { return outpoint; } + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(outpoint); + READWRITE(mapMasternodeVotes); + READWRITE(fAttacked); + } + bool AddVote(const CTxLockVote& vote); std::vector GetVotes() const; bool HasMasternodeVoted(const COutPoint& outpointMasternodeIn) const; @@ -292,6 +342,11 @@ class CTxLockCandidate int64_t nTimeCreated; public: + CTxLockCandidate() : + nConfirmedHeight(-1), + nTimeCreated(GetTime()) + {} + CTxLockCandidate(const CTxLockRequest& txLockRequestIn) : nConfirmedHeight(-1), nTimeCreated(GetTime()), @@ -302,6 +357,16 @@ class CTxLockCandidate CTxLockRequest txLockRequest; std::map mapOutPointLocks; + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(txLockRequest); + READWRITE(mapOutPointLocks); + READWRITE(nTimeCreated); + READWRITE(nConfirmedHeight); + } + uint256 GetHash() const { return txLockRequest.GetHash(); } void AddOutPointLock(const COutPoint& outpoint); diff --git a/src/keepass.cpp b/src/keepass.cpp index 6cb791a4299a..8d02b3e6750c 100644 --- a/src/keepass.cpp +++ b/src/keepass.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/keepass.h b/src/keepass.h index d94f6f5dc7dc..928dbc101068 100644 --- a/src/keepass.h +++ b/src/keepass.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 58081c4d1baa..1856e6db6c23 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -15,7 +15,9 @@ #include "spork.h" #include "util.h" -#include +#include "evo/deterministicmns.h" + +#include /** Object for who's going to get paid on which blocks */ CMasternodePayments mnpayments; @@ -24,6 +26,49 @@ CCriticalSection cs_vecPayees; CCriticalSection cs_mapMasternodeBlocks; CCriticalSection cs_mapMasternodePaymentVotes; +bool IsOldBudgetBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockReward, std::string& strErrorRet) { + const Consensus::Params& consensusParams = Params().GetConsensus(); + bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward); + + if (nBlockHeight < consensusParams.nBudgetPaymentsStartBlock) { + strErrorRet = strprintf("Incorrect block %d, old budgets are not activated yet", nBlockHeight); + return false; + } + + if (nBlockHeight >= consensusParams.nSuperblockStartBlock) { + strErrorRet = strprintf("Incorrect block %d, old budgets are no longer active", nBlockHeight); + return false; + } + + // we are still using budgets, but we have no data about them anymore, + // all we know is predefined budget cycle and window + + int nOffset = nBlockHeight % consensusParams.nBudgetPaymentsCycleBlocks; + if(nBlockHeight >= consensusParams.nBudgetPaymentsStartBlock && + nOffset < consensusParams.nBudgetPaymentsWindowBlocks) { + // NOTE: old budget system is disabled since 12.1 + if(masternodeSync.IsSynced()) { + // no old budget blocks should be accepted here on mainnet, + // testnet/devnet/regtest should produce regular blocks only + LogPrint("gobject", "%s -- WARNING: Client synced but old budget system is disabled, checking block value against block reward\n", __func__); + if(!isBlockRewardValueMet) { + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, old budgets are disabled", + nBlockHeight, block.vtx[0]->GetValueOut(), blockReward); + } + return isBlockRewardValueMet; + } + // when not synced, rely on online nodes (all networks) + LogPrint("gobject", "%s -- WARNING: Skipping old budget block value checks, accepting block\n", __func__); + return true; + } + // LogPrint("gobject", "%s -- Block is not in budget cycle window, checking block value against block reward\n", __func__); + if(!isBlockRewardValueMet) { + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, block is not in old budget cycle window", + nBlockHeight, block.vtx[0]->GetValueOut(), blockReward); + } + return isBlockRewardValueMet; +} + /** * IsBlockValueValid * @@ -37,107 +82,94 @@ CCriticalSection cs_mapMasternodePaymentVotes; bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockReward, std::string& strErrorRet) { - strErrorRet = ""; - + const Consensus::Params& consensusParams = Params().GetConsensus(); bool isBlockRewardValueMet = (block.vtx[0]->GetValueOut() <= blockReward); - if(fDebug) LogPrintf("block.vtx[0]->GetValueOut() %lld <= blockReward %lld\n", block.vtx[0]->GetValueOut(), blockReward); - - // we are still using budgets, but we have no data about them anymore, - // all we know is predefined budget cycle and window - const Consensus::Params& consensusParams = Params().GetConsensus(); + strErrorRet = ""; - if(nBlockHeight < consensusParams.nSuperblockStartBlock) { - int nOffset = nBlockHeight % consensusParams.nBudgetPaymentsCycleBlocks; - if(nBlockHeight >= consensusParams.nBudgetPaymentsStartBlock && - nOffset < consensusParams.nBudgetPaymentsWindowBlocks) { - // NOTE: old budget system is disabled since 12.1 - if(masternodeSync.IsSynced()) { - // no old budget blocks should be accepted here on mainnet, - // testnet/devnet/regtest should produce regular blocks only - LogPrint("gobject", "IsBlockValueValid -- WARNING: Client synced but old budget system is disabled, checking block value against block reward\n"); - if(!isBlockRewardValueMet) { - strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, old budgets are disabled", - nBlockHeight, block.vtx[0]->GetValueOut(), blockReward); - } - return isBlockRewardValueMet; - } - // when not synced, rely on online nodes (all networks) - LogPrint("gobject", "IsBlockValueValid -- WARNING: Skipping old budget block value checks, accepting block\n"); - return true; - } - // LogPrint("gobject", "IsBlockValueValid -- Block is not in budget cycle window, checking block value against block reward\n"); + if (nBlockHeight < consensusParams.nBudgetPaymentsStartBlock) { + // old budget system is not activated yet, just make sure we do not exceed the regular block reward if(!isBlockRewardValueMet) { - strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, block is not in old budget cycle window", + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, old budgets are not activated yet", nBlockHeight, block.vtx[0]->GetValueOut(), blockReward); } return isBlockRewardValueMet; + } else if (nBlockHeight < consensusParams.nSuperblockStartBlock) { + // superblocks are not enabled yet, check if we can pass old budget rules + return IsOldBudgetBlockValueValid(block, nBlockHeight, blockReward, strErrorRet); } - // superblocks started + if(fDebug) LogPrintf("block.vtx[0]->GetValueOut() %lld <= blockReward %lld\n", block.vtx[0]->GetValueOut(), blockReward); CAmount nSuperblockMaxValue = blockReward + CSuperblock::GetPaymentsLimit(nBlockHeight); bool isSuperblockMaxValueMet = (block.vtx[0]->GetValueOut() <= nSuperblockMaxValue); LogPrint("gobject", "block.vtx[0]->GetValueOut() %lld <= nSuperblockMaxValue %lld\n", block.vtx[0]->GetValueOut(), nSuperblockMaxValue); - if(!masternodeSync.IsSynced() || fLiteMode) { - // not enough data but at least it must NOT exceed superblock max value - if(CSuperblock::IsValidBlockHeight(nBlockHeight)) { - if(fDebug) LogPrintf("IsBlockPayeeValid -- WARNING: Not enough data, checking superblock max bounds only\n"); - if(!isSuperblockMaxValueMet) { - strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded superblock max value", - nBlockHeight, block.vtx[0]->GetValueOut(), nSuperblockMaxValue); - } - return isSuperblockMaxValueMet; - } - if(!isBlockRewardValueMet) { + if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) { + // can't possibly be a superblock, so lets just check for block reward limits + if (!isBlockRewardValueMet) { strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, only regular blocks are allowed at this height", nBlockHeight, block.vtx[0]->GetValueOut(), blockReward); } - // it MUST be a regular block otherwise return isBlockRewardValueMet; } - // we are synced, let's try to check as much data as we can + // bail out in case superblock limits were exceeded + if (!isSuperblockMaxValueMet) { + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded superblock max value", + nBlockHeight, block.vtx[0]->GetValueOut(), nSuperblockMaxValue); + return false; + } - if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { - if(CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { - if(CSuperblockManager::IsValid(*block.vtx[0], nBlockHeight, blockReward)) { - LogPrint("gobject", "IsBlockValueValid -- Valid superblock at height %d: %s", nBlockHeight, block.vtx[0]->ToString()); - // all checks are done in CSuperblock::IsValid, nothing to do here - return true; - } + if(!masternodeSync.IsSynced() || fLiteMode) { + if(fDebug) LogPrintf("%s -- WARNING: Not enough data, checked superblock max bounds only\n", __func__); + // not enough data for full checks but at least we know that the superblock limits were honored. + // We rely on the network to have followed the correct chain in this case + return true; + } - // triggered but invalid? that's weird - LogPrintf("IsBlockValueValid -- ERROR: Invalid superblock detected at height %d: %s", nBlockHeight, block.vtx[0]->ToString()); - // should NOT allow invalid superblocks, when superblocks are enabled - strErrorRet = strprintf("invalid superblock detected at height %d", nBlockHeight); - return false; - } - LogPrint("gobject", "IsBlockValueValid -- No triggered superblock detected at height %d\n", nBlockHeight); + // we are synced and possibly on a superblock now + + if (!sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { + // should NOT allow superblocks at all, when superblocks are disabled + // revert to block reward limits in this case + LogPrint("gobject", "%s -- Superblocks are disabled, no superblocks allowed\n", __func__); if(!isBlockRewardValueMet) { - strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, no triggered superblock detected", + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, superblocks are disabled", nBlockHeight, block.vtx[0]->GetValueOut(), blockReward); } - } else { - // should NOT allow superblocks at all, when superblocks are disabled - LogPrint("gobject", "IsBlockValueValid -- Superblocks are disabled, no superblocks allowed\n"); + return isBlockRewardValueMet; + } + + if (!CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { + // we are on a valid superblock height but a superblock was not triggered + // revert to block reward limits in this case if(!isBlockRewardValueMet) { - strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, superblocks are disabled", + strErrorRet = strprintf("coinbase pays too much at height %d (actual=%d vs limit=%d), exceeded block reward, no triggered superblock detected", nBlockHeight, block.vtx[0]->GetValueOut(), blockReward); } + return isBlockRewardValueMet; } - // it MUST be a regular block - return isBlockRewardValueMet; + // this actually also checks for correct payees and not only amount + if (!CSuperblockManager::IsValid(*block.vtx[0], nBlockHeight, blockReward)) { + // triggered but invalid? that's weird + LogPrintf("%s -- ERROR: Invalid superblock detected at height %d: %s", __func__, nBlockHeight, block.vtx[0]->ToString()); + // should NOT allow invalid superblocks, when superblocks are enabled + strErrorRet = strprintf("invalid superblock detected at height %d", nBlockHeight); + return false; + } + + // we got a valid superblock + return true; } bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward) { - if(!masternodeSync.IsSynced() || fLiteMode) { + if((!masternodeSync.IsSynced() && !deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) || fLiteMode) { //there is no budget data to use to check anything, let's just accept the longest chain - if(fDebug) LogPrintf("IsBlockPayeeValid -- WARNING: Not enough data, skipping block payee checks\n"); + if(fDebug) LogPrintf("%s -- WARNING: Not enough data, skipping block payee checks\n", __func__); return true; } @@ -150,7 +182,7 @@ bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount bloc // NOTE: old budget system is disabled since 12.1 and we should never enter this branch // anymore when sync is finished (on mainnet). We have no old budget data but these blocks // have tons of confirmations and can be safely accepted without payee verification - LogPrint("gobject", "IsBlockPayeeValid -- WARNING: Client synced but old budget system is disabled, accepting any payee\n"); + LogPrint("gobject", "%s -- WARNING: Client synced but old budget system is disabled, accepting any payee\n", __func__); return true; } @@ -160,54 +192,84 @@ bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount bloc if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED)) { if(CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { if(CSuperblockManager::IsValid(txNew, nBlockHeight, blockReward)) { - LogPrint("gobject", "IsBlockPayeeValid -- Valid superblock at height %d: %s", nBlockHeight, txNew.ToString()); - return true; + LogPrint("gobject", "%s -- Valid superblock at height %d: %s", __func__, nBlockHeight, txNew.ToString()); + // only allow superblock and masternode payments in the same block after spork15 activation + if (!deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) + return true; + // continue validation, should also pay MN + } else { + LogPrintf("%s -- ERROR: Invalid superblock detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); + // should NOT allow such superblocks, when superblocks are enabled + return false; } - - LogPrintf("IsBlockPayeeValid -- ERROR: Invalid superblock detected at height %d: %s", nBlockHeight, txNew.ToString()); - // should NOT allow such superblocks, when superblocks are enabled - return false; + } else { + LogPrint("gobject", "%s -- No triggered superblock detected at height %d\n", __func__, nBlockHeight); } - // continue validation, should pay MN - LogPrint("gobject", "IsBlockPayeeValid -- No triggered superblock detected at height %d\n", nBlockHeight); } else { // should NOT allow superblocks at all, when superblocks are disabled - LogPrint("gobject", "IsBlockPayeeValid -- Superblocks are disabled, no superblocks allowed\n"); + LogPrint("gobject", "%s -- Superblocks are disabled, no superblocks allowed\n", __func__); } - // IF THIS ISN'T A SUPERBLOCK OR SUPERBLOCK IS INVALID, IT SHOULD PAY A MASTERNODE DIRECTLY - if(mnpayments.IsTransactionValid(txNew, nBlockHeight)) { - LogPrint("mnpayments", "IsBlockPayeeValid -- Valid masternode payment at height %d: %s", nBlockHeight, txNew.ToString()); + // If this isn't a superblock or spork15 is activated, check for correct masternode payment + if(mnpayments.IsTransactionValid(txNew, nBlockHeight, blockReward)) { + LogPrint("mnpayments", "%s -- Valid masternode payment at height %d: %s", __func__, nBlockHeight, txNew.ToString()); return true; } - if(sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { - LogPrintf("IsBlockPayeeValid -- ERROR: Invalid masternode payment detected at height %d: %s", nBlockHeight, txNew.ToString()); + if (deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) { + // always enforce masternode payments when spork15 is active return false; - } + } else { + if(sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT)) { + LogPrintf("%s -- ERROR: Invalid masternode payment detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); + return false; + } - LogPrintf("IsBlockPayeeValid -- WARNING: Masternode payment enforcement is disabled, accepting any payee\n"); - return true; + LogPrintf("%s-- WARNING: Masternode payment enforcement is disabled, accepting any payee\n", __func__); + return true; + } } -void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutMasternodeRet, std::vector& voutSuperblockRet) +void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet, std::vector& voutSuperblockPaymentsRet) { // only create superblocks if spork is enabled AND if superblock is actually triggered // (height should be validated inside) if(sporkManager.IsSporkActive(SPORK_9_SUPERBLOCKS_ENABLED) && CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { - LogPrint("gobject", "FillBlockPayments -- triggered superblock creation at height %d\n", nBlockHeight); - CSuperblockManager::CreateSuperblock(txNew, nBlockHeight, voutSuperblockRet); - return; + LogPrint("gobject", "%s -- triggered superblock creation at height %d\n", __func__, nBlockHeight); + CSuperblockManager::GetSuperblockPayments(nBlockHeight, voutSuperblockPaymentsRet); } - // FILL BLOCK PAYEE WITH MASTERNODE PAYMENT OTHERWISE - mnpayments.FillBlockPayee(txNew, nBlockHeight, blockReward, txoutMasternodeRet); - LogPrint("mnpayments", "FillBlockPayments -- nBlockHeight %d blockReward %lld txoutMasternodeRet %s txNew %s", - nBlockHeight, blockReward, txoutMasternodeRet.ToString(), txNew.ToString()); + bool allowSuperblockAndMNReward = deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight); + + // don't allow payments to superblocks AND masternodes before spork15 activation + if (!voutSuperblockPaymentsRet.empty() && !allowSuperblockAndMNReward) { + txNew.vout.insert(txNew.vout.end(), voutSuperblockPaymentsRet.begin(), voutSuperblockPaymentsRet.end()); + return; + } + + if (!mnpayments.GetMasternodeTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) { + // no idea whom to pay (MN list empty?), lets hope for the best + return; + } + + txNew.vout.insert(txNew.vout.end(), voutMasternodePaymentsRet.begin(), voutMasternodePaymentsRet.end()); + txNew.vout.insert(txNew.vout.end(), voutSuperblockPaymentsRet.begin(), voutSuperblockPaymentsRet.end()); + + std::string voutMasternodeStr; + for (const auto& txout : voutMasternodePaymentsRet) { + // subtract MN payment from miner reward + txNew.vout[0].nValue -= txout.nValue; + if (!voutMasternodeStr.empty()) + voutMasternodeStr += ","; + voutMasternodeStr += txout.ToString(); + } + + LogPrint("mnpayments", "%s -- nBlockHeight %d blockReward %lld voutMasternodePaymentsRet \"%s\" txNew %s", __func__, + nBlockHeight, blockReward, voutMasternodeStr, txNew.ToString()); } -std::string GetRequiredPaymentsString(int nBlockHeight) +std::string GetLegacyRequiredPaymentsString(int nBlockHeight) { // IF WE HAVE A ACTIVATED TRIGGER FOR THIS HEIGHT - IT IS A SUPERBLOCK, GET THE REQUIRED PAYEES if(CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { @@ -218,6 +280,54 @@ std::string GetRequiredPaymentsString(int nBlockHeight) return mnpayments.GetRequiredPaymentsString(nBlockHeight); } +std::string GetRequiredPaymentsString(int nBlockHeight, const CDeterministicMNCPtr &payee) +{ + std::string strPayee = "Unknown"; + if (payee) { + CTxDestination dest; + if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) + assert(false); + strPayee = CBitcoinAddress(dest).ToString(); + } + if (CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { + strPayee += ", " + CSuperblockManager::GetRequiredPaymentsString(nBlockHeight); + } + return strPayee; +} + +std::map GetRequiredPaymentsStrings(int nStartHeight, int nEndHeight) +{ + std::map mapPayments; + + LOCK(cs_main); + int nChainTipHeight = chainActive.Height(); + + bool doProjection = false; + for(int h = nStartHeight; h < nEndHeight; h++) { + if (deterministicMNManager->IsDeterministicMNsSporkActive(h)) { + if (h <= nChainTipHeight) { + auto payee = deterministicMNManager->GetListForBlock(chainActive[h - 1]->GetBlockHash()).GetMNPayee(); + mapPayments.emplace(h, GetRequiredPaymentsString(h, payee)); + } else { + doProjection = true; + break; + } + } else { + mapPayments.emplace(h, GetLegacyRequiredPaymentsString(h)); + } + } + if (doProjection) { + auto projection = deterministicMNManager->GetListAtChainTip().GetProjectedMNPayees(nEndHeight - nChainTipHeight); + for (size_t i = 0; i < projection.size(); i++) { + auto payee = projection[i]; + int h = nChainTipHeight + 1 + i; + mapPayments.emplace(h, GetRequiredPaymentsString(h, payee)); + } + } + + return mapPayments; +} + void CMasternodePayments::Clear() { LOCK2(cs_mapMasternodeBlocks, cs_mapMasternodePaymentVotes); @@ -227,6 +337,9 @@ void CMasternodePayments::Clear() bool CMasternodePayments::UpdateLastVote(const CMasternodePaymentVote& vote) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return false; + LOCK(cs_mapMasternodePaymentVotes); const auto it = mapMasternodesLastVote.find(vote.masternodeOutpoint); @@ -243,45 +356,42 @@ bool CMasternodePayments::UpdateLastVote(const CMasternodePaymentVote& vote) } /** -* FillBlockPayee +* GetMasternodeTxOuts * -* Fill Masternode ONLY payment block +* Get masternode payment tx outputs */ -void CMasternodePayments::FillBlockPayee(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutMasternodeRet) const +bool CMasternodePayments::GetMasternodeTxOuts(int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const { // make sure it's not filled yet - txoutMasternodeRet = CTxOut(); + voutMasternodePaymentsRet.clear(); - CScript payee; + if(!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePaymentsRet)) { + assert(!deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)); - if(!GetBlockPayee(nBlockHeight, payee)) { // no masternode detected... int nCount = 0; masternode_info_t mnInfo; if(!mnodeman.GetNextMasternodeInQueueForPayment(nBlockHeight, true, nCount, mnInfo)) { // ...and we can't calculate it on our own - LogPrintf("CMasternodePayments::FillBlockPayee -- Failed to detect masternode to pay\n"); - return; + LogPrintf("CMasternodePayments::%s -- Failed to detect masternode to pay\n", __func__); + return false; } // fill payee with locally calculated winner and hope for the best - payee = GetScriptForDestination(mnInfo.pubKeyCollateralAddress.GetID()); + CScript payee = GetScriptForDestination(mnInfo.keyIDCollateralAddress); + CAmount masternodePayment = GetMasternodePayment(nBlockHeight, blockReward); + voutMasternodePaymentsRet.emplace_back(masternodePayment, payee); } - // GET MASTERNODE PAYMENT VARIABLES SETUP - CAmount masternodePayment = GetMasternodePayment(nBlockHeight, blockReward); - - // split reward between miner ... - txNew.vout[0].nValue -= masternodePayment; - // ... and masternode - txoutMasternodeRet = CTxOut(masternodePayment, payee); - txNew.vout.push_back(txoutMasternodeRet); + for (const auto& txout : voutMasternodePaymentsRet) { + CTxDestination address1; + ExtractDestination(txout.scriptPubKey, address1); + CBitcoinAddress address2(address1); - CTxDestination address1; - ExtractDestination(payee, address1); - CBitcoinAddress address2(address1); + LogPrintf("CMasternodePayments::%s -- Masternode payment %lld to %s\n", __func__, txout.nValue, address2.ToString()); + } - LogPrintf("CMasternodePayments::FillBlockPayee -- Masternode payment %lld to %s\n", masternodePayment, address2.ToString()); + return true; } int CMasternodePayments::GetMinMasternodePaymentsProto() const { @@ -292,7 +402,10 @@ int CMasternodePayments::GetMinMasternodePaymentsProto() const { void CMasternodePayments::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { - if(fLiteMode) return; // disable all Absolute specific functionality + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + + if(fLiteMode) return; // disable all Dash specific functionality if (strCommand == NetMsgType::MASTERNODEPAYMENTSYNC) { //Masternode Payments Request Sync @@ -308,12 +421,6 @@ void CMasternodePayments::ProcessMessage(CNode* pfrom, const std::string& strCom // but this is a heavy one so it's better to finish sync first. if (!masternodeSync.IsSynced()) return; - // DEPRECATED, should be removed on next protocol bump - if(pfrom->nVersion == 70208) { - int nCountNeeded; - vRecv >> nCountNeeded; - } - if(netfulfilledman.HasFulfilledRequest(pfrom->addr, NetMsgType::MASTERNODEPAYMENTSYNC)) { LOCK(cs_main); // Asking for the payments list multiple times in a short period of time is no good @@ -385,7 +492,7 @@ void CMasternodePayments::ProcessMessage(CNode* pfrom, const std::string& strCom } int nDos = 0; - if(!vote.CheckSignature(mnInfo.pubKeyMasternode, nCachedBlockHeight, nDos)) { + if(!vote.CheckSignature(mnInfo.legacyKeyIDOperator, nCachedBlockHeight, nDos)) { if(nDos) { LOCK(cs_main); LogPrintf("MASTERNODEPAYMENTVOTE -- ERROR: invalid signature\n"); @@ -445,27 +552,27 @@ bool CMasternodePaymentVote::Sign() if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if(!CHashSigner::SignHash(hash, activeMasternode.keyMasternode, vchSig)) { - LogPrintf("CMasternodePaymentVote::Sign -- SignHash() failed\n"); + if(!CHashSigner::SignHash(hash, activeMasternodeInfo.legacyKeyOperator, vchSig)) { + LogPrintf("CMasternodePaymentVote::%s -- SignHash() failed\n", __func__); return false; } - if (!CHashSigner::VerifyHash(hash, activeMasternode.pubKeyMasternode, vchSig, strError)) { - LogPrintf("CMasternodePaymentVote::Sign -- VerifyHash() failed, error: %s\n", strError); + if (!CHashSigner::VerifyHash(hash, activeMasternodeInfo.legacyKeyIDOperator, vchSig, strError)) { + LogPrintf("CMasternodePaymentVote::%s -- VerifyHash() failed, error: %s\n", __func__, strError); return false; } } else { std::string strMessage = masternodeOutpoint.ToStringShort() + - boost::lexical_cast(nBlockHeight) + + std::to_string(nBlockHeight) + ScriptToAsmStr(payee); - if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternode.keyMasternode)) { - LogPrintf("CMasternodePaymentVote::Sign -- SignMessage() failed\n"); + if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternodeInfo.legacyKeyOperator)) { + LogPrintf("CMasternodePaymentVote::%s -- SignMessage() failed\n", __func__); return false; } - if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, vchSig, strMessage, strError)) { - LogPrintf("CMasternodePaymentVote::Sign -- VerifyMessage() failed, error: %s\n", strError); + if(!CMessageSigner::VerifyMessage(activeMasternodeInfo.legacyKeyIDOperator, vchSig, strMessage, strError)) { + LogPrintf("CMasternodePaymentVote::%s -- VerifyMessage() failed, error: %s\n", __func__, strError); return false; } } @@ -473,12 +580,43 @@ bool CMasternodePaymentVote::Sign() return true; } -bool CMasternodePayments::GetBlockPayee(int nBlockHeight, CScript& payeeRet) const +bool CMasternodePayments::GetBlockTxOuts(int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const { - LOCK(cs_mapMasternodeBlocks); + voutMasternodePaymentsRet.clear(); - auto it = mapMasternodeBlocks.find(nBlockHeight); - return it != mapMasternodeBlocks.end() && it->second.GetBestPayee(payeeRet); + CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward); + + if (deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) { + uint256 blockHash; + { + LOCK(cs_main); + blockHash = chainActive[nBlockHeight - 1]->GetBlockHash(); + } + uint256 proTxHash; + auto dmnPayee = deterministicMNManager->GetListForBlock(blockHash).GetMNPayee(); + if (!dmnPayee) { + return false; + } + + if (dmnPayee->nOperatorReward == 0 || dmnPayee->pdmnState->scriptOperatorPayout == CScript()) { + voutMasternodePaymentsRet.emplace_back(masternodeReward, dmnPayee->pdmnState->scriptPayout); + } else { + CAmount operatorReward = (masternodeReward * dmnPayee->nOperatorReward) / 10000; + masternodeReward -= operatorReward; + voutMasternodePaymentsRet.emplace_back(masternodeReward, dmnPayee->pdmnState->scriptPayout); + voutMasternodePaymentsRet.emplace_back(operatorReward, dmnPayee->pdmnState->scriptOperatorPayout); + } + return true; + } else { + LOCK(cs_mapMasternodeBlocks); + auto it = mapMasternodeBlocks.find(nBlockHeight); + CScript payee; + if (it == mapMasternodeBlocks.end() || !it->second.GetBestPayee(payee)) { + return false; + } + voutMasternodePaymentsRet.emplace_back(masternodeReward, payee); + return true; + } } // Is this masternode scheduled to get paid soon? @@ -487,16 +625,29 @@ bool CMasternodePayments::IsScheduled(const masternode_info_t& mnInfo, int nNotB { LOCK(cs_mapMasternodeBlocks); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + auto projectedPayees = deterministicMNManager->GetListAtChainTip().GetProjectedMNPayees(8); + for (const auto &dmn : projectedPayees) { + if (dmn->proTxHash == mnInfo.outpoint.hash) { + return true; + } + } + return false; + } + if(!masternodeSync.IsMasternodeListSynced()) return false; CScript mnpayee; - mnpayee = GetScriptForDestination(mnInfo.pubKeyCollateralAddress.GetID()); + mnpayee = GetScriptForDestination(mnInfo.keyIDCollateralAddress); - CScript payee; for(int64_t h = nCachedBlockHeight; h <= nCachedBlockHeight + 8; h++){ if(h == nNotBlockHeight) continue; - if(GetBlockPayee(h, payee) && mnpayee == payee) { - return true; + std::vector voutMasternodePayments; + if(GetBlockTxOuts(h, 0, voutMasternodePayments)) { + for (const auto& txout : voutMasternodePayments) { + if (txout.scriptPubKey == mnpayee) + return true; + } } } @@ -519,7 +670,7 @@ bool CMasternodePayments::AddOrUpdatePaymentVote(const CMasternodePaymentVote& v auto it = mapMasternodeBlocks.emplace(vote.nBlockHeight, CMasternodeBlockPayees(vote.nBlockHeight)).first; it->second.AddPayee(vote); - LogPrint("mnpayments", "CMasternodePayments::AddOrUpdatePaymentVote -- added, hash=%s\n", nVoteHash.ToString()); + LogPrint("mnpayments", "CMasternodePayments::%s -- added, hash=%s\n", __func__, nVoteHash.ToString()); return true; } @@ -552,7 +703,7 @@ bool CMasternodeBlockPayees::GetBestPayee(CScript& payeeRet) const LOCK(cs_vecPayees); if(vecPayees.empty()) { - LogPrint("mnpayments", "CMasternodeBlockPayees::GetBestPayee -- ERROR: couldn't find any payee\n"); + LogPrint("mnpayments", "CMasternodeBlockPayees::%s -- ERROR: couldn't find any payee\n", __func__); return false; } @@ -577,7 +728,7 @@ bool CMasternodeBlockPayees::HasPayeeWithVotes(const CScript& payeeIn, int nVote } } - LogPrint("mnpayments", "CMasternodeBlockPayees::HasPayeeWithVotes -- ERROR: couldn't find any payee with %d+ votes\n", nVotesReq); + LogPrint("mnpayments", "CMasternodeBlockPayees::%s -- ERROR: couldn't find any payee with %d+ votes\n", __func__, nVotesReq); return false; } @@ -605,7 +756,7 @@ bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew) const if (payee.GetVoteCount() >= MNPAYMENTS_SIGNATURES_REQUIRED) { for (const auto& txout : txNew.vout) { if (payee.GetPayee() == txout.scriptPubKey && nMasternodePayment == txout.nValue) { - LogPrint("mnpayments", "CMasternodeBlockPayees::IsTransactionValid -- Found required payment\n"); + LogPrint("mnpayments", "CMasternodeBlockPayees::%s -- Found required payment\n", __func__); return true; } } @@ -622,7 +773,7 @@ bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew) const } } - LogPrintf("CMasternodeBlockPayees::IsTransactionValid -- ERROR: Missing required payment, possible payees: '%s', amount: %f ABS\n", strPayeesPossible, (float)nMasternodePayment/COIN); + LogPrintf("CMasternodeBlockPayees::%s -- ERROR: Missing required payment, possible payees: '%s', amount: %f ABSOLUTE\n", __func__, strPayeesPossible, (float)nMasternodePayment/COIN); return false; } @@ -653,21 +804,49 @@ std::string CMasternodeBlockPayees::GetRequiredPaymentsString() const std::string CMasternodePayments::GetRequiredPaymentsString(int nBlockHeight) const { LOCK(cs_mapMasternodeBlocks); - const auto it = mapMasternodeBlocks.find(nBlockHeight); return it == mapMasternodeBlocks.end() ? "Unknown" : it->second.GetRequiredPaymentsString(); } -bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlockHeight) const +bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward) const { - LOCK(cs_mapMasternodeBlocks); + if (deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) { + std::vector voutMasternodePayments; + if (!GetBlockTxOuts(nBlockHeight, blockReward, voutMasternodePayments)) { + LogPrintf("CMasternodePayments::%s -- ERROR failed to get payees for block at height %s\n", __func__, nBlockHeight); + return true; + } - const auto it = mapMasternodeBlocks.find(nBlockHeight); - return it == mapMasternodeBlocks.end() ? true : it->second.IsTransactionValid(txNew); + for (const auto& txout : voutMasternodePayments) { + bool found = false; + for (const auto& txout2 : txNew.vout) { + if (txout == txout2) { + found = true; + break; + } + } + if (!found) { + CTxDestination dest; + if (!ExtractDestination(txout.scriptPubKey, dest)) + assert(false); + LogPrintf("CMasternodePayments::%s -- ERROR failed to find expected payee %s in block at height %s\n", __func__, CBitcoinAddress(dest).ToString(), nBlockHeight); + return false; + } + } + return true; + } else { + LOCK(cs_mapMasternodeBlocks); + const auto it = mapMasternodeBlocks.find(nBlockHeight); + return it == mapMasternodeBlocks.end() ? true : it->second.IsTransactionValid(txNew); + } } void CMasternodePayments::CheckAndRemove() { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + return; + } + if(!masternodeSync.IsBlockchainSynced()) return; LOCK2(cs_mapMasternodeBlocks, cs_mapMasternodePaymentVotes); @@ -679,14 +858,14 @@ void CMasternodePayments::CheckAndRemove() CMasternodePaymentVote vote = (*it).second; if(nCachedBlockHeight - vote.nBlockHeight > nLimit) { - LogPrint("mnpayments", "CMasternodePayments::CheckAndRemove -- Removing old Masternode payment: nBlockHeight=%d\n", vote.nBlockHeight); + LogPrint("mnpayments", "CMasternodePayments::%s -- Removing old Masternode payment: nBlockHeight=%d\n", __func__, vote.nBlockHeight); mapMasternodePaymentVotes.erase(it++); mapMasternodeBlocks.erase(vote.nBlockHeight); } else { ++it; } } - LogPrintf("CMasternodePayments::CheckAndRemove -- %s\n", ToString()); + LogPrintf("CMasternodePayments::%s -- %s\n", __func__, ToString()); } bool CMasternodePaymentVote::IsValid(CNode* pnode, int nValidationHeight, std::string& strError, CConnman& connman) const @@ -724,7 +903,7 @@ bool CMasternodePaymentVote::IsValid(CNode* pnode, int nValidationHeight, std::s int nRank; if(!mnodeman.GetMasternodeRank(masternodeOutpoint, nRank, nBlockHeight - 101, nMinRequiredProtocol)) { - LogPrint("mnpayments", "CMasternodePaymentVote::IsValid -- Can't calculate rank for masternode %s\n", + LogPrint("mnpayments", "CMasternodePaymentVote::%s -- Can't calculate rank for masternode %s\n", __func__, masternodeOutpoint.ToStringShort()); return false; } @@ -737,7 +916,7 @@ bool CMasternodePaymentVote::IsValid(CNode* pnode, int nValidationHeight, std::s if(nRank > MNPAYMENTS_SIGNATURES_TOTAL*2 && nBlockHeight > nValidationHeight) { LOCK(cs_main); strError = strprintf("Masternode %s is not in the top %d (%d)", masternodeOutpoint.ToStringShort(), MNPAYMENTS_SIGNATURES_TOTAL*2, nRank); - LogPrintf("CMasternodePaymentVote::IsValid -- Error: %s\n", strError); + LogPrintf("CMasternodePaymentVote::%s -- Error: %s\n", __func__, strError); Misbehaving(pnode->GetId(), 20); } // Still invalid however @@ -749,6 +928,10 @@ bool CMasternodePaymentVote::IsValid(CNode* pnode, int nValidationHeight, std::s bool CMasternodePayments::ProcessBlock(int nBlockHeight, CConnman& connman) { + if (deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) { + return true; + } + // DETERMINE IF WE SHOULD BE VOTING FOR THE NEXT PAYEE if(fLiteMode || !fMasternodeMode) return false; @@ -760,48 +943,48 @@ bool CMasternodePayments::ProcessBlock(int nBlockHeight, CConnman& connman) int nRank; - if (!mnodeman.GetMasternodeRank(activeMasternode.outpoint, nRank, nBlockHeight - 101, GetMinMasternodePaymentsProto())) { - LogPrint("mnpayments", "CMasternodePayments::ProcessBlock -- Unknown Masternode\n"); + if (!mnodeman.GetMasternodeRank(activeMasternodeInfo.outpoint, nRank, nBlockHeight - 101, GetMinMasternodePaymentsProto())) { + LogPrint("mnpayments", "CMasternodePayments::%s -- Unknown Masternode\n", __func__); return false; } if (nRank > MNPAYMENTS_SIGNATURES_TOTAL) { - LogPrint("mnpayments", "CMasternodePayments::ProcessBlock -- Masternode not in the top %d (%d)\n", MNPAYMENTS_SIGNATURES_TOTAL, nRank); + LogPrint("mnpayments", "CMasternodePayments::%s -- Masternode not in the top %d (%d)\n", __func__, MNPAYMENTS_SIGNATURES_TOTAL, nRank); return false; } // LOCATE THE NEXT MASTERNODE WHICH SHOULD BE PAID - LogPrintf("CMasternodePayments::ProcessBlock -- Start: nBlockHeight=%d, masternode=%s\n", nBlockHeight, activeMasternode.outpoint.ToStringShort()); + LogPrintf("CMasternodePayments::%s -- Start: nBlockHeight=%d, masternode=%s\n", __func__, nBlockHeight, activeMasternodeInfo.outpoint.ToStringShort()); // pay to the oldest MN that still had no payment but its input is old enough and it was active long enough int nCount = 0; masternode_info_t mnInfo; if (!mnodeman.GetNextMasternodeInQueueForPayment(nBlockHeight, true, nCount, mnInfo)) { - LogPrintf("CMasternodePayments::ProcessBlock -- ERROR: Failed to find masternode to pay\n"); + LogPrintf("CMasternodePayments::%s -- ERROR: Failed to find masternode to pay\n", __func__); return false; } - LogPrintf("CMasternodePayments::ProcessBlock -- Masternode found by GetNextMasternodeInQueueForPayment(): %s\n", mnInfo.outpoint.ToStringShort()); + LogPrintf("CMasternodePayments::%s -- Masternode found by GetNextMasternodeInQueueForPayment(): %s\n", __func__, mnInfo.outpoint.ToStringShort()); - CScript payee = GetScriptForDestination(mnInfo.pubKeyCollateralAddress.GetID()); + CScript payee = GetScriptForDestination(mnInfo.keyIDCollateralAddress); - CMasternodePaymentVote voteNew(activeMasternode.outpoint, nBlockHeight, payee); + CMasternodePaymentVote voteNew(activeMasternodeInfo.outpoint, nBlockHeight, payee); CTxDestination address1; ExtractDestination(payee, address1); CBitcoinAddress address2(address1); - LogPrintf("CMasternodePayments::ProcessBlock -- vote: payee=%s, nBlockHeight=%d\n", address2.ToString(), nBlockHeight); + LogPrintf("CMasternodePayments::%s -- vote: payee=%s, nBlockHeight=%d\n", __func__, address2.ToString(), nBlockHeight); // SIGN MESSAGE TO NETWORK WITH OUR MASTERNODE KEYS - LogPrintf("CMasternodePayments::ProcessBlock -- Signing vote\n"); + LogPrintf("CMasternodePayments::%s -- Signing vote\n", __func__); if (voteNew.Sign()) { - LogPrintf("CMasternodePayments::ProcessBlock -- AddOrUpdatePaymentVote()\n"); + LogPrintf("CMasternodePayments::%s -- AddOrUpdatePaymentVote()\n", __func__); if (AddOrUpdatePaymentVote(voteNew)) { voteNew.Relay(connman); @@ -818,13 +1001,13 @@ void CMasternodePayments::CheckBlockVotes(int nBlockHeight) CMasternodeMan::rank_pair_vec_t mns; if (!mnodeman.GetMasternodeRanks(mns, nBlockHeight - 101, GetMinMasternodePaymentsProto())) { - LogPrintf("CMasternodePayments::CheckBlockVotes -- nBlockHeight=%d, GetMasternodeRanks failed\n", nBlockHeight); + LogPrintf("CMasternodePayments::%s -- nBlockHeight=%d, GetMasternodeRanks failed\n", __func__, nBlockHeight); return; } std::string debugStr; - debugStr += strprintf("CMasternodePayments::CheckBlockVotes -- nBlockHeight=%d,\n Expected voting MNs:\n", nBlockHeight); + debugStr += strprintf("CMasternodePayments::%s -- nBlockHeight=%d,\n Expected voting MNs:\n", __func__, nBlockHeight); LOCK2(cs_mapMasternodeBlocks, cs_mapMasternodePaymentVotes); @@ -884,9 +1067,13 @@ void CMasternodePayments::CheckBlockVotes(int nBlockHeight) void CMasternodePaymentVote::Relay(CConnman& connman) const { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + return; + } + // Do not relay until fully synced if(!masternodeSync.IsSynced()) { - LogPrint("mnpayments", "CMasternodePayments::Relay -- won't relay until fully synced\n"); + LogPrint("mnpayments", "CMasternodePayments::%s -- won't relay until fully synced\n", __func__); return; } @@ -894,7 +1081,7 @@ void CMasternodePaymentVote::Relay(CConnman& connman) const connman.RelayInv(inv); } -bool CMasternodePaymentVote::CheckSignature(const CPubKey& pubKeyMasternode, int nValidationHeight, int &nDos) const +bool CMasternodePaymentVote::CheckSignature(const CKeyID& keyIDOperator, int nValidationHeight, int &nDos) const { // do not ban by default nDos = 0; @@ -903,12 +1090,12 @@ bool CMasternodePaymentVote::CheckSignature(const CPubKey& pubKeyMasternode, int if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyIDOperator, vchSig, strError)) { // could be a signature in old format std::string strMessage = masternodeOutpoint.ToStringShort() + - boost::lexical_cast(nBlockHeight) + + std::to_string(nBlockHeight) + ScriptToAsmStr(payee); - if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { // nope, not in old format either // Only ban for future block vote when we are already synced. // Otherwise it could be the case when MN which signed this vote is using another key now @@ -922,10 +1109,10 @@ bool CMasternodePaymentVote::CheckSignature(const CPubKey& pubKeyMasternode, int } } else { std::string strMessage = masternodeOutpoint.ToStringShort() + - boost::lexical_cast(nBlockHeight) + + std::to_string(nBlockHeight) + ScriptToAsmStr(payee); - if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { // Only ban for future block vote when we are already synced. // Otherwise it could be the case when MN which signed this vote is using another key now // and we have no idea about the old one. @@ -975,7 +1162,7 @@ void CMasternodePayments::Sync(CNode* pnode, CConnman& connman) const } } - LogPrintf("CMasternodePayments::Sync -- Sent %d votes to peer=%d\n", nInvCount, pnode->id); + LogPrintf("CMasternodePayments::%s -- Sent %d votes to peer=%d\n", __func__, nInvCount, pnode->id); CNetMsgMaker msgMaker(pnode->GetSendVersion()); connman.PushMessage(pnode, msgMaker.Make(NetMsgType::SYNCSTATUSCOUNT, MASTERNODE_SYNC_MNW, nInvCount)); } @@ -1000,7 +1187,7 @@ void CMasternodePayments::RequestLowDataPaymentBlocks(CNode* pnode, CConnman& co vToFetch.push_back(CInv(MSG_MASTERNODE_PAYMENT_BLOCK, pindex->GetBlockHash())); // We should not violate GETDATA rules if(vToFetch.size() == MAX_INV_SZ) { - LogPrintf("CMasternodePayments::RequestLowDataPaymentBlocks -- asking peer=%d for %d blocks\n", pnode->id, MAX_INV_SZ); + LogPrintf("CMasternodePayments::%s -- asking peer=%d for %d blocks\n", __func__, pnode->id, MAX_INV_SZ); connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETDATA, vToFetch)); // Start filling new batch vToFetch.clear(); @@ -1010,12 +1197,11 @@ void CMasternodePayments::RequestLowDataPaymentBlocks(CNode* pnode, CConnman& co pindex = pindex->pprev; } - auto it = mapMasternodeBlocks.begin(); - - while(it != mapMasternodeBlocks.end()) { + for (auto& mnBlockPayees : mapMasternodeBlocks) { + int nBlockHeight = mnBlockPayees.first; int nTotalVotes = 0; bool fFound = false; - for (const auto& payee : it->second.vecPayees) { + for (const auto& payee : mnBlockPayees.second.vecPayees) { if(payee.GetVoteCount() >= MNPAYMENTS_SIGNATURES_REQUIRED) { fFound = true; break; @@ -1026,38 +1212,36 @@ void CMasternodePayments::RequestLowDataPaymentBlocks(CNode* pnode, CConnman& co // or no clear winner was found but there are at least avg number of votes if(fFound || nTotalVotes >= (MNPAYMENTS_SIGNATURES_TOTAL + MNPAYMENTS_SIGNATURES_REQUIRED)/2) { // so just move to the next block - ++it; continue; } // DEBUG DBG ( // Let's see why this failed - for (const auto& payee : it->second.vecPayees) { + for (const auto& payee : mnBlockPayees.second.vecPayees) { CTxDestination address1; ExtractDestination(payee.GetPayee(), address1); CBitcoinAddress address2(address1); printf("payee %s votes %d\n", address2.ToString().c_str(), payee.GetVoteCount()); } - printf("block %d votes total %d\n", it->first, nTotalVotes); + printf("block %d votes total %d\n", nBlockHeight, nTotalVotes); ) // END DEBUG // Low data block found, let's try to sync it uint256 hash; - if(GetBlockHash(hash, it->first)) { + if(GetBlockHash(hash, nBlockHeight)) { vToFetch.push_back(CInv(MSG_MASTERNODE_PAYMENT_BLOCK, hash)); } // We should not violate GETDATA rules if(vToFetch.size() == MAX_INV_SZ) { - LogPrintf("CMasternodePayments::RequestLowDataPaymentBlocks -- asking peer=%d for %d payment blocks\n", pnode->id, MAX_INV_SZ); + LogPrintf("CMasternodePayments::%s -- asking peer=%d for %d payment blocks\n", __func__, pnode->id, MAX_INV_SZ); connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETDATA, vToFetch)); // Start filling new batch vToFetch.clear(); } - ++it; } // Ask for the rest of it if(!vToFetch.empty()) { - LogPrintf("CMasternodePayments::RequestLowDataPaymentBlocks -- asking peer=%d for %d payment blocks\n", pnode->id, vToFetch.size()); + LogPrintf("CMasternodePayments::%s -- asking peer=%d for %d payment blocks\n", __func__, pnode->id, vToFetch.size()); connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETDATA, vToFetch)); } } @@ -1088,8 +1272,12 @@ void CMasternodePayments::UpdatedBlockTip(const CBlockIndex *pindex, CConnman& c { if(!pindex) return; + if (deterministicMNManager->IsDeterministicMNsSporkActive(pindex->nHeight)) { + return; + } + nCachedBlockHeight = pindex->nHeight; - LogPrint("mnpayments", "CMasternodePayments::UpdatedBlockTip -- nCachedBlockHeight=%d\n", nCachedBlockHeight); + LogPrint("mnpayments", "CMasternodePayments::%s -- nCachedBlockHeight=%d\n", __func__, nCachedBlockHeight); int nFutureBlock = nCachedBlockHeight + 10; diff --git a/src/masternode-payments.h b/src/masternode-payments.h index 640742bca73b..57108c388245 100644 --- a/src/masternode-payments.h +++ b/src/masternode-payments.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -24,20 +24,19 @@ static const int MNPAYMENTS_SIGNATURES_TOTAL = 10; // vote for masternode and be elected as a payment winner // V1 - Last protocol version before update // V2 - Newest protocol version -static const int MIN_MASTERNODE_PAYMENT_PROTO_VERSION_1 = 70209; -static const int MIN_MASTERNODE_PAYMENT_PROTO_VERSION_2 = 70210; +static const int MIN_MASTERNODE_PAYMENT_PROTO_VERSION_1 = 70210; +static const int MIN_MASTERNODE_PAYMENT_PROTO_VERSION_2 = 70211; extern CCriticalSection cs_vecPayees; extern CCriticalSection cs_mapMasternodeBlocks; -extern CCriticalSection cs_mapMasternodePayeeVotes; extern CMasternodePayments mnpayments; /// TODO: all 4 functions do not belong here really, they should be refactored/moved somewhere (main.cpp ?) bool IsBlockValueValid(const CBlock& block, int nBlockHeight, CAmount blockReward, std::string& strErrorRet); bool IsBlockPayeeValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward); -void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutMasternodeRet, std::vector& voutSuperblockRet); -std::string GetRequiredPaymentsString(int nBlockHeight); +void FillBlockPayments(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet, std::vector& voutSuperblockPaymentsRet); +std::map GetRequiredPaymentsStrings(int nStartHeight, int nEndHeight); class CMasternodePayee { @@ -134,21 +133,7 @@ class CMasternodePaymentVote template inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (nVersion == 70209 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin{}; - if (ser_action.ForRead()) { - READWRITE(txin); - masternodeOutpoint = txin.prevout; - } else { - txin = CTxIn(masternodeOutpoint); - READWRITE(txin); - } - } else { - // using new format directly - READWRITE(masternodeOutpoint); - } + READWRITE(masternodeOutpoint); READWRITE(nBlockHeight); READWRITE(*(CScriptBase*)(&payee)); if (!(s.GetType() & SER_GETHASH)) { @@ -160,7 +145,7 @@ class CMasternodePaymentVote uint256 GetSignatureHash() const; bool Sign(); - bool CheckSignature(const CPubKey& pubKeyMasternode, int nValidationHeight, int &nDos) const; + bool CheckSignature(const CKeyID& keyIDOperator, int nValidationHeight, int &nDos) const; bool IsValid(CNode* pnode, int nValidationHeight, std::string& strError, CConnman& connman) const; void Relay(CConnman& connman) const; @@ -214,8 +199,8 @@ class CMasternodePayments void RequestLowDataPaymentBlocks(CNode* pnode, CConnman& connman) const; void CheckAndRemove(); - bool GetBlockPayee(int nBlockHeight, CScript& payeeRet) const; - bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight) const; + bool GetBlockTxOuts(int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const; + bool IsTransactionValid(const CTransaction& txNew, int nBlockHeight, CAmount blockReward) const; bool IsScheduled(const masternode_info_t& mnInfo, int nNotBlockHeight) const; bool UpdateLastVote(const CMasternodePaymentVote& vote); @@ -223,7 +208,7 @@ class CMasternodePayments int GetMinMasternodePaymentsProto() const; void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); std::string GetRequiredPaymentsString(int nBlockHeight) const; - void FillBlockPayee(CMutableTransaction& txNew, int nBlockHeight, CAmount blockReward, CTxOut& txoutMasternodeRet) const; + bool GetMasternodeTxOuts(int nBlockHeight, CAmount blockReward, std::vector& voutMasternodePaymentsRet) const; std::string ToString() const; int GetBlockCount() const { return mapMasternodeBlocks.size(); } @@ -233,6 +218,8 @@ class CMasternodePayments int GetStorageLimit() const; void UpdatedBlockTip(const CBlockIndex *pindex, CConnman& connman); + + void DoMaintenance() { CheckAndRemove(); } }; #endif diff --git a/src/masternode-sync.cpp b/src/masternode-sync.cpp index 0f16e806d3e9..dc701790e80f 100644 --- a/src/masternode-sync.cpp +++ b/src/masternode-sync.cpp @@ -1,21 +1,18 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "activemasternode.h" -#include "checkpoints.h" #include "governance.h" #include "validation.h" -#include "masternode.h" #include "masternode-payments.h" #include "masternode-sync.h" #include "masternodeman.h" #include "netfulfilledman.h" #include "netmessagemaker.h" -#include "spork.h" #include "ui_interface.h" -#include "util.h" +#include "evo/deterministicmns.h" class CMasternodeSync; CMasternodeSync masternodeSync; @@ -23,13 +20,13 @@ CMasternodeSync masternodeSync; void CMasternodeSync::Fail() { nTimeLastFailure = GetTime(); - nRequestedMasternodeAssets = MASTERNODE_SYNC_FAILED; + nCurrentAsset = MASTERNODE_SYNC_FAILED; } void CMasternodeSync::Reset() { - nRequestedMasternodeAssets = MASTERNODE_SYNC_INITIAL; - nRequestedMasternodeAttempt = 0; + nCurrentAsset = MASTERNODE_SYNC_INITIAL; + nTriedPeerCount = 0; nTimeAssetSyncStarted = GetTime(); nTimeLastBumped = GetTime(); nTimeLastFailure = 0; @@ -44,7 +41,7 @@ void CMasternodeSync::BumpAssetLastTime(const std::string& strFuncName) std::string CMasternodeSync::GetAssetName() { - switch(nRequestedMasternodeAssets) + switch(nCurrentAsset) { case(MASTERNODE_SYNC_INITIAL): return "MASTERNODE_SYNC_INITIAL"; case(MASTERNODE_SYNC_WAITING): return "MASTERNODE_SYNC_WAITING"; @@ -59,36 +56,44 @@ std::string CMasternodeSync::GetAssetName() void CMasternodeSync::SwitchToNextAsset(CConnman& connman) { - switch(nRequestedMasternodeAssets) + switch(nCurrentAsset) { case(MASTERNODE_SYNC_FAILED): throw std::runtime_error("Can't switch to next asset from failed, should use Reset() first!"); break; case(MASTERNODE_SYNC_INITIAL): - nRequestedMasternodeAssets = MASTERNODE_SYNC_WAITING; + nCurrentAsset = MASTERNODE_SYNC_WAITING; LogPrintf("CMasternodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); break; case(MASTERNODE_SYNC_WAITING): LogPrintf("CMasternodeSync::SwitchToNextAsset -- Completed %s in %llds\n", GetAssetName(), GetTime() - nTimeAssetSyncStarted); - nRequestedMasternodeAssets = MASTERNODE_SYNC_LIST; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + nCurrentAsset = MASTERNODE_SYNC_GOVERNANCE; + } else { + nCurrentAsset = MASTERNODE_SYNC_LIST; + } LogPrintf("CMasternodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); break; case(MASTERNODE_SYNC_LIST): LogPrintf("CMasternodeSync::SwitchToNextAsset -- Completed %s in %llds\n", GetAssetName(), GetTime() - nTimeAssetSyncStarted); - nRequestedMasternodeAssets = MASTERNODE_SYNC_MNW; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + nCurrentAsset = MASTERNODE_SYNC_GOVERNANCE; + } else { + nCurrentAsset = MASTERNODE_SYNC_MNW; + } LogPrintf("CMasternodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); break; case(MASTERNODE_SYNC_MNW): LogPrintf("CMasternodeSync::SwitchToNextAsset -- Completed %s in %llds\n", GetAssetName(), GetTime() - nTimeAssetSyncStarted); - nRequestedMasternodeAssets = MASTERNODE_SYNC_GOVERNANCE; + nCurrentAsset = MASTERNODE_SYNC_GOVERNANCE; LogPrintf("CMasternodeSync::SwitchToNextAsset -- Starting %s\n", GetAssetName()); break; case(MASTERNODE_SYNC_GOVERNANCE): LogPrintf("CMasternodeSync::SwitchToNextAsset -- Completed %s in %llds\n", GetAssetName(), GetTime() - nTimeAssetSyncStarted); - nRequestedMasternodeAssets = MASTERNODE_SYNC_FINISHED; + nCurrentAsset = MASTERNODE_SYNC_FINISHED; uiInterface.NotifyAdditionalDataSyncProgressChanged(1); //try to activate our masternode if possible - activeMasternode.ManageState(connman); + legacyActiveMasternodeManager.ManageState(connman); connman.ForEachNode(CConnman::AllNodes, [](CNode* pnode) { netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); @@ -97,14 +102,14 @@ void CMasternodeSync::SwitchToNextAsset(CConnman& connman) break; } - nRequestedMasternodeAttempt = 0; + nTriedPeerCount = 0; nTimeAssetSyncStarted = GetTime(); BumpAssetLastTime("CMasternodeSync::SwitchToNextAsset"); } std::string CMasternodeSync::GetSyncStatus() { - switch (masternodeSync.nRequestedMasternodeAssets) { + switch (masternodeSync.nCurrentAsset) { case MASTERNODE_SYNC_INITIAL: return _("Synchroning blockchain..."); case MASTERNODE_SYNC_WAITING: return _("Synchronization pending..."); case MASTERNODE_SYNC_LIST: return _("Synchronizing masternodes..."); @@ -134,7 +139,7 @@ void CMasternodeSync::ProcessMessage(CNode* pfrom, const std::string& strCommand void CMasternodeSync::ProcessTick(CConnman& connman) { static int nTick = 0; - if(nTick++ % MASTERNODE_SYNC_TICK_SECONDS != 0) return; + nTick++; // reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode) static int64_t nTimeLastProcess = GetTime(); @@ -166,8 +171,8 @@ void CMasternodeSync::ProcessTick(CConnman& connman) } // Calculate "progress" for LOG reporting / GUI notification - double nSyncProgress = double(nRequestedMasternodeAttempt + (nRequestedMasternodeAssets - 1) * 8) / (8*4); - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d nRequestedMasternodeAttempt %d nSyncProgress %f\n", nTick, nRequestedMasternodeAssets, nRequestedMasternodeAttempt, nSyncProgress); + double nSyncProgress = double(nTriedPeerCount + (nCurrentAsset - 1) * 8) / (8*4); + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d nTriedPeerCount %d nSyncProgress %f\n", nTick, nCurrentAsset, nTriedPeerCount, nSyncProgress); uiInterface.NotifyAdditionalDataSyncProgressChanged(nSyncProgress); std::vector vNodesCopy = connman.CopyNodeVector(CConnman::FullyConnectedOnly); @@ -185,22 +190,23 @@ void CMasternodeSync::ProcessTick(CConnman& connman) // QUICK MODE (REGTEST ONLY!) if(Params().NetworkIDString() == CBaseChainParams::REGTEST) { - if(nRequestedMasternodeAttempt <= 2) { + if (nCurrentAsset == MASTERNODE_SYNC_WAITING) { connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); //get current network sporks - } else if(nRequestedMasternodeAttempt < 4) { - mnodeman.DsegUpdate(pnode, connman); - } else if(nRequestedMasternodeAttempt < 6) { - //sync payment votes - if(pnode->nVersion == 70208) { - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTSYNC, mnpayments.GetStorageLimit())); //sync payment votes - } else { + SwitchToNextAsset(connman); + } else if (nCurrentAsset == MASTERNODE_SYNC_LIST) { + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) { + mnodeman.DsegUpdate(pnode, connman); + } + SwitchToNextAsset(connman); + } else if (nCurrentAsset == MASTERNODE_SYNC_MNW) { + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) { connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTSYNC)); //sync payment votes } + SwitchToNextAsset(connman); + } else if (nCurrentAsset == MASTERNODE_SYNC_GOVERNANCE) { SendGovernanceSyncRequest(pnode, connman); - } else { - nRequestedMasternodeAssets = MASTERNODE_SYNC_FINISHED; + SwitchToNextAsset(connman); } - nRequestedMasternodeAttempt++; connman.ReleaseNodeVector(vNodesCopy); return; } @@ -222,12 +228,12 @@ void CMasternodeSync::ProcessTick(CConnman& connman) netfulfilledman.AddFulfilledRequest(pnode->addr, "spork-sync"); // get current network sporks connman.PushMessage(pnode, msgMaker.Make(NetMsgType::GETSPORKS)); - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d -- requesting sporks from peer=%d\n", nTick, nRequestedMasternodeAssets, pnode->id); + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- requesting sporks from peer=%d\n", nTick, nCurrentAsset, pnode->id); } // INITIAL TIMEOUT - if(nRequestedMasternodeAssets == MASTERNODE_SYNC_WAITING) { + if(nCurrentAsset == MASTERNODE_SYNC_WAITING) { if(GetTime() - nTimeLastBumped > MASTERNODE_SYNC_TIMEOUT_SECONDS) { // At this point we know that: // a) there are peers (because we are looping on at least one of them); @@ -243,12 +249,18 @@ void CMasternodeSync::ProcessTick(CConnman& connman) // MNLIST : SYNC MASTERNODE LIST FROM OTHER CONNECTED CLIENTS - if(nRequestedMasternodeAssets == MASTERNODE_SYNC_LIST) { - LogPrint("masternode", "CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d nTimeLastBumped %lld GetTime() %lld diff %lld\n", nTick, nRequestedMasternodeAssets, nTimeLastBumped, GetTime(), GetTime() - nTimeLastBumped); + if(nCurrentAsset == MASTERNODE_SYNC_LIST) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + SwitchToNextAsset(connman); + connman.ReleaseNodeVector(vNodesCopy); + return; + } + + LogPrint("masternode", "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d nTimeLastBumped %lld GetTime() %lld diff %lld\n", nTick, nCurrentAsset, nTimeLastBumped, GetTime(), GetTime() - nTimeLastBumped); // check for timeout first if(GetTime() - nTimeLastBumped > MASTERNODE_SYNC_TIMEOUT_SECONDS) { - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d -- timeout\n", nTick, nRequestedMasternodeAssets); - if (nRequestedMasternodeAttempt == 0) { + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- timeout\n", nTick, nCurrentAsset); + if (nTriedPeerCount == 0) { LogPrintf("CMasternodeSync::ProcessTick -- ERROR: failed to sync %s\n", GetAssetName()); // there is no way we can continue without masternode list, fail here and try later Fail(); @@ -261,7 +273,7 @@ void CMasternodeSync::ProcessTick(CConnman& connman) } // request from three peers max - if (nRequestedMasternodeAttempt > 2) { + if (nTriedPeerCount > 2) { connman.ReleaseNodeVector(vNodesCopy); return; } @@ -271,7 +283,7 @@ void CMasternodeSync::ProcessTick(CConnman& connman) netfulfilledman.AddFulfilledRequest(pnode->addr, "masternode-list-sync"); if (pnode->nVersion < mnpayments.GetMinMasternodePaymentsProto()) continue; - nRequestedMasternodeAttempt++; + nTriedPeerCount++; mnodeman.DsegUpdate(pnode, connman); @@ -281,14 +293,20 @@ void CMasternodeSync::ProcessTick(CConnman& connman) // MNW : SYNC MASTERNODE PAYMENT VOTES FROM OTHER CONNECTED CLIENTS - if(nRequestedMasternodeAssets == MASTERNODE_SYNC_MNW) { - LogPrint("mnpayments", "CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d nTimeLastBumped %lld GetTime() %lld diff %lld\n", nTick, nRequestedMasternodeAssets, nTimeLastBumped, GetTime(), GetTime() - nTimeLastBumped); + if(nCurrentAsset == MASTERNODE_SYNC_MNW) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + SwitchToNextAsset(connman); + connman.ReleaseNodeVector(vNodesCopy); + return; + } + + LogPrint("mnpayments", "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d nTimeLastBumped %lld GetTime() %lld diff %lld\n", nTick, nCurrentAsset, nTimeLastBumped, GetTime(), GetTime() - nTimeLastBumped); // check for timeout first // This might take a lot longer than MASTERNODE_SYNC_TIMEOUT_SECONDS due to new blocks, // but that should be OK and it should timeout eventually. if(GetTime() - nTimeLastBumped > MASTERNODE_SYNC_TIMEOUT_SECONDS) { - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d -- timeout\n", nTick, nRequestedMasternodeAssets); - if (nRequestedMasternodeAttempt == 0) { + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- timeout\n", nTick, nCurrentAsset); + if (nTriedPeerCount == 0) { LogPrintf("CMasternodeSync::ProcessTick -- ERROR: failed to sync %s\n", GetAssetName()); // probably not a good idea to proceed without winner list Fail(); @@ -303,15 +321,15 @@ void CMasternodeSync::ProcessTick(CConnman& connman) // check for data // if mnpayments already has enough blocks and votes, switch to the next asset // try to fetch data from at least two peers though - if(nRequestedMasternodeAttempt > 1 && mnpayments.IsEnoughData()) { - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d -- found enough data\n", nTick, nRequestedMasternodeAssets); + if(nTriedPeerCount > 1 && mnpayments.IsEnoughData()) { + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- found enough data\n", nTick, nCurrentAsset); SwitchToNextAsset(connman); connman.ReleaseNodeVector(vNodesCopy); return; } // request from three peers max - if (nRequestedMasternodeAttempt > 2) { + if (nTriedPeerCount > 2) { connman.ReleaseNodeVector(vNodesCopy); return; } @@ -321,15 +339,10 @@ void CMasternodeSync::ProcessTick(CConnman& connman) netfulfilledman.AddFulfilledRequest(pnode->addr, "masternode-payment-sync"); if(pnode->nVersion < mnpayments.GetMinMasternodePaymentsProto()) continue; - nRequestedMasternodeAttempt++; + nTriedPeerCount++; // ask node for all payment votes it has (new nodes will only return votes for future payments) - //sync payment votes - if(pnode->nVersion == 70208) { - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTSYNC, mnpayments.GetStorageLimit())); - } else { - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTSYNC)); - } + connman.PushMessage(pnode, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTSYNC)); // ask node for missing pieces only (old nodes will not be asked) mnpayments.RequestLowDataPaymentBlocks(pnode, connman); @@ -339,13 +352,13 @@ void CMasternodeSync::ProcessTick(CConnman& connman) // GOVOBJ : SYNC GOVERNANCE ITEMS FROM OUR PEERS - if(nRequestedMasternodeAssets == MASTERNODE_SYNC_GOVERNANCE) { - LogPrint("gobject", "CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d nTimeLastBumped %lld GetTime() %lld diff %lld\n", nTick, nRequestedMasternodeAssets, nTimeLastBumped, GetTime(), GetTime() - nTimeLastBumped); + if(nCurrentAsset == MASTERNODE_SYNC_GOVERNANCE) { + LogPrint("gobject", "CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d nTimeLastBumped %lld GetTime() %lld diff %lld\n", nTick, nCurrentAsset, nTimeLastBumped, GetTime(), GetTime() - nTimeLastBumped); // check for timeout first if(GetTime() - nTimeLastBumped > MASTERNODE_SYNC_TIMEOUT_SECONDS) { - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d -- timeout\n", nTick, nRequestedMasternodeAssets); - if(nRequestedMasternodeAttempt == 0) { + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- timeout\n", nTick, nCurrentAsset); + if(nTriedPeerCount == 0) { LogPrintf("CMasternodeSync::ProcessTick -- WARNING: failed to sync %s\n", GetAssetName()); // it's kind of ok to skip this for now, hopefully we'll catch up later? } @@ -375,7 +388,7 @@ void CMasternodeSync::ProcessTick(CConnman& connman) // after that and less then 0.01% or MASTERNODE_SYNC_TICK_SECONDS // (i.e. 1 per second) votes were recieved during the last tick. // We can be pretty sure that we are done syncing. - LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d -- asked for all objects, nothing to do\n", nTick, nRequestedMasternodeAssets); + LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nCurrentAsset %d -- asked for all objects, nothing to do\n", nTick, nCurrentAsset); // reset nTimeNoObjectsLeft to be able to use the same condition on resync nTimeNoObjectsLeft = 0; SwitchToNextAsset(connman); @@ -390,7 +403,7 @@ void CMasternodeSync::ProcessTick(CConnman& connman) netfulfilledman.AddFulfilledRequest(pnode->addr, "governance-sync"); if (pnode->nVersion < MIN_GOVERNANCE_PEER_PROTO_VERSION) continue; - nRequestedMasternodeAttempt++; + nTriedPeerCount++; SendGovernanceSyncRequest(pnode, connman); @@ -484,7 +497,7 @@ void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitia if (!IsBlockchainSynced() && fReachedBestHeader) { if (fLiteMode) { // nothing to do in lite mode, just finish the process immediately - nRequestedMasternodeAssets = MASTERNODE_SYNC_FINISHED; + nCurrentAsset = MASTERNODE_SYNC_FINISHED; return; } // Reached best header while being in initial mode. diff --git a/src/masternode-sync.h b/src/masternode-sync.h index d3a63ce7b46b..dcf7a5fe4308 100644 --- a/src/masternode-sync.h +++ b/src/masternode-sync.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef MASTERNODE_SYNC_H @@ -8,8 +8,6 @@ #include "chain.h" #include "net.h" -#include - class CMasternodeSync; static const int MASTERNODE_SYNC_FAILED = -1; @@ -25,8 +23,6 @@ static const int MASTERNODE_SYNC_FINISHED = 999; static const int MASTERNODE_SYNC_TICK_SECONDS = 6; static const int MASTERNODE_SYNC_TIMEOUT_SECONDS = 30; // our blocks are 2 minutes so 30 seconds should be fine -static const int MASTERNODE_SYNC_ENOUGH_PEERS = 6; - extern CMasternodeSync masternodeSync; // @@ -37,9 +33,9 @@ class CMasternodeSync { private: // Keep track of current asset - int nRequestedMasternodeAssets; + int nCurrentAsset; // Count peers we've requested the asset from - int nRequestedMasternodeAttempt; + int nTriedPeerCount; // Time when current masternode asset sync started int64_t nTimeAssetSyncStarted; @@ -56,14 +52,14 @@ class CMasternodeSync void SendGovernanceSyncRequest(CNode* pnode, CConnman& connman); - bool IsFailed() { return nRequestedMasternodeAssets == MASTERNODE_SYNC_FAILED; } - bool IsBlockchainSynced() { return nRequestedMasternodeAssets > MASTERNODE_SYNC_WAITING; } - bool IsMasternodeListSynced() { return nRequestedMasternodeAssets > MASTERNODE_SYNC_LIST; } - bool IsWinnersListSynced() { return nRequestedMasternodeAssets > MASTERNODE_SYNC_MNW; } - bool IsSynced() { return nRequestedMasternodeAssets == MASTERNODE_SYNC_FINISHED; } + bool IsFailed() { return nCurrentAsset == MASTERNODE_SYNC_FAILED; } + bool IsBlockchainSynced() { return nCurrentAsset > MASTERNODE_SYNC_WAITING; } + bool IsMasternodeListSynced() { return nCurrentAsset > MASTERNODE_SYNC_LIST; } + bool IsWinnersListSynced() { return nCurrentAsset > MASTERNODE_SYNC_MNW; } + bool IsSynced() { return nCurrentAsset == MASTERNODE_SYNC_FINISHED; } - int GetAssetID() { return nRequestedMasternodeAssets; } - int GetAttempt() { return nRequestedMasternodeAttempt; } + int GetAssetID() { return nCurrentAsset; } + int GetAttempt() { return nTriedPeerCount; } void BumpAssetLastTime(const std::string& strFuncName); int64_t GetAssetStartTime() { return nTimeAssetSyncStarted; } std::string GetAssetName(); @@ -78,6 +74,8 @@ class CMasternodeSync void AcceptedBlockHeader(const CBlockIndex *pindexNew); void NotifyHeaderTip(const CBlockIndex *pindexNew, bool fInitialDownload, CConnman& connman); void UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitialDownload, CConnman& connman); + + void DoMaintenance(CConnman &connman) { ProcessTick(connman); } }; #endif diff --git a/src/masternode.cpp b/src/masternode.cpp index a7c9941e4456..f242de6d30f8 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -19,7 +19,9 @@ #include "wallet/wallet.h" #endif // ENABLE_WALLET -#include +#include "evo/deterministicmns.h" + +#include CMasternode::CMasternode() : @@ -27,9 +29,9 @@ CMasternode::CMasternode() : fAllowMixingTx(true) {} -CMasternode::CMasternode(CService addr, COutPoint outpoint, CPubKey pubKeyCollateralAddress, CPubKey pubKeyMasternode, int nProtocolVersionIn) : +CMasternode::CMasternode(CService addr, COutPoint outpoint, CPubKey pubKeyCollateralAddressNew, CPubKey pubKeyMasternodeNew, int nProtocolVersionIn) : masternode_info_t{ MASTERNODE_ENABLED, nProtocolVersionIn, GetAdjustedTime(), - outpoint, addr, pubKeyCollateralAddress, pubKeyMasternode}, + outpoint, addr, pubKeyCollateralAddressNew, pubKeyMasternodeNew}, fAllowMixingTx(true) {} @@ -53,6 +55,17 @@ CMasternode::CMasternode(const CMasternodeBroadcast& mnb) : fAllowMixingTx(true) {} +CMasternode::CMasternode(const uint256 &proTxHash, const CDeterministicMNCPtr& dmn) : + masternode_info_t{ MASTERNODE_ENABLED, dmn->pdmnState->nProtocolVersion, GetAdjustedTime(), + COutPoint(proTxHash, dmn->nCollateralIndex), dmn->pdmnState->addr, CKeyID(), dmn->pdmnState->keyIDOwner, dmn->pdmnState->pubKeyOperator, dmn->pdmnState->keyIDVoting}, + fAllowMixingTx(true) +{ + CTxDestination dest; + if (!ExtractDestination(dmn->pdmnState->scriptPayout, dest) || !boost::get(&dest)) + assert(false); // should not happen (previous verification forbids non p2pkh/p2pk + keyIDCollateralAddress = *boost::get(&dest); +} + // // When a new masternode broadcast is sent, update our information // @@ -61,6 +74,9 @@ bool CMasternode::UpdateFromNewBroadcast(CMasternodeBroadcast& mnb, CConnman& co if(mnb.sigTime <= sigTime && !mnb.fRecovery) return false; pubKeyMasternode = mnb.pubKeyMasternode; + keyIDOwner = mnb.pubKeyMasternode.GetID(); + legacyKeyIDOperator = mnb.pubKeyMasternode.GetID(); + keyIDVoting = mnb.pubKeyMasternode.GetID(); sigTime = mnb.sigTime; vchSig = mnb.vchSig; nProtocolVersion = mnb.nProtocolVersion; @@ -74,11 +90,11 @@ bool CMasternode::UpdateFromNewBroadcast(CMasternodeBroadcast& mnb, CConnman& co mnodeman.mapSeenMasternodePing.insert(std::make_pair(lastPing.GetHash(), lastPing)); } // if it matches our Masternode privkey... - if(fMasternodeMode && pubKeyMasternode == activeMasternode.pubKeyMasternode) { + if(fMasternodeMode && legacyKeyIDOperator == activeMasternodeInfo.legacyKeyIDOperator) { nPoSeBanScore = -MASTERNODE_POSE_BAN_MAX_SCORE; if(nProtocolVersion == PROTOCOL_VERSION) { // ... and PROTOCOL_VERSION, then we've been remotely activated ... - activeMasternode.ManageState(connman); + legacyActiveMasternodeManager.ManageState(connman); } else { // ... otherwise we need to reactivate our node, do not add it to the list and do not relay // but also do not ban the node we get this message from @@ -102,13 +118,13 @@ arith_uint256 CMasternode::CalculateScore(const uint256& blockHash) const return UintToArith256(ss.GetHash()); } -CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outpoint, const CPubKey& pubkey) +CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outpoint, const CKeyID& keyID) { int nHeight; - return CheckCollateral(outpoint, pubkey, nHeight); + return CheckCollateral(outpoint, keyID, nHeight); } -CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outpoint, const CPubKey& pubkey, int& nHeightRet) +CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outpoint, const CKeyID& keyID, int& nHeightRet) { AssertLockHeld(cs_main); @@ -121,9 +137,26 @@ CMasternode::CollateralStatus CMasternode::CheckCollateral(const COutPoint& outp return COLLATERAL_INVALID_AMOUNT; } - if(pubkey == CPubKey() || coin.out.scriptPubKey != GetScriptForDestination(pubkey.GetID())) { + if(keyID.IsNull() || coin.out.scriptPubKey != GetScriptForDestination(keyID)) { return COLLATERAL_INVALID_PUBKEY; } + + CTransactionRef tx; + uint256 hashBlock; + if (!GetTransaction(outpoint.hash, tx, Params().GetConsensus(), hashBlock, true)) { + // should not happen + return COLLATERAL_UTXO_NOT_FOUND; + } + if (tx->nType != TRANSACTION_PROVIDER_REGISTER) { + assert(mapBlockIndex.count(hashBlock)); + + CBlockIndex *pindex = mapBlockIndex[hashBlock]; + if (VersionBitsState(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_AIP0003, versionbitscache) == THRESHOLD_ACTIVE) { + LogPrintf("CMasternode::CheckCollateral -- ERROR: Collateral of masternode %s was created after AIP3 activation and is not a ProTx\n", outpoint.ToStringShort()); + return COLLATERAL_UTXO_NOT_PROTX; + } + } + nHeightRet = coin.nHeight; return COLLATERAL_OK; } @@ -171,7 +204,7 @@ void CMasternode::Check(bool fForce) } int nActiveStatePrev = nActiveState; - bool fOurMasternode = fMasternodeMode && activeMasternode.pubKeyMasternode == pubKeyMasternode; + bool fOurMasternode = fMasternodeMode && activeMasternodeInfo.legacyKeyIDOperator == legacyKeyIDOperator; // masternode doesn't meet payment protocol requirements ... bool fRequireUpdate = nProtocolVersion < mnpayments.GetMinMasternodePaymentsProto() || @@ -315,11 +348,25 @@ std::string CMasternode::GetStatus() const void CMasternode::UpdateLastPaid(const CBlockIndex *pindex, int nMaxBlocksToScanBack) { + AssertLockHeld(cs_main); + if(!pindex) return; + if (deterministicMNManager->IsDeterministicMNsSporkActive(pindex->nHeight)) { + auto dmn = deterministicMNManager->GetListForBlock(pindex->GetBlockHash()).GetMN(outpoint.hash); + if (!dmn || dmn->pdmnState->nLastPaidHeight == -1) { + LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- not found\n", outpoint.ToStringShort()); + } else { + nBlockLastPaid = (int)dmn->pdmnState->nLastPaidHeight; + nTimeLastPaid = chainActive[nBlockLastPaid]->nTime; + LogPrint("masternode", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s -- found new %d\n", outpoint.ToStringShort(), nBlockLastPaid); + } + return; + } + const CBlockIndex *BlockReading = pindex; - CScript mnpayee = GetScriptForDestination(pubKeyCollateralAddress.GetID()); + CScript mnpayee = GetScriptForDestination(keyIDCollateralAddress); // LogPrint("mnpayments", "CMasternode::UpdateLastPaidBlock -- searching for block with payment to %s\n", outpoint.ToStringShort()); LOCK(cs_mapMasternodeBlocks); @@ -343,7 +390,7 @@ void CMasternode::UpdateLastPaid(const CBlockIndex *pindex, int nMaxBlocksToScan } } - if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + if (BlockReading->pprev == nullptr) { assert(BlockReading); break; } BlockReading = BlockReading->pprev; } @@ -396,7 +443,7 @@ bool CMasternodeBroadcast::Create(const COutPoint& outpoint, const CService& ser // wait for reindex and/or import to finish if (fImporting || fReindex) return false; - LogPrint("masternode", "CMasternodeBroadcast::Create -- pubKeyCollateralAddressNew = %s, pubKeyMasternodeNew.GetID() = %s\n", + LogPrint("masternode", "CMasternodeBroadcast::Create -- pubKeyCollateralAddressNew = %s, keyIDOperator = %s\n", CBitcoinAddress(pubKeyCollateralAddressNew.GetID()).ToString(), pubKeyMasternodeNew.GetID().ToString()); @@ -409,7 +456,7 @@ bool CMasternodeBroadcast::Create(const COutPoint& outpoint, const CService& ser }; CMasternodePing mnp(outpoint); - if (!mnp.Sign(keyMasternodeNew, pubKeyMasternodeNew)) + if (!mnp.Sign(keyMasternodeNew, pubKeyMasternodeNew.GetID())) return Log(strprintf("Failed to sign ping, masternode=%s", outpoint.ToStringShort())); mnbRet = CMasternodeBroadcast(service, outpoint, pubKeyCollateralAddressNew, pubKeyMasternodeNew, PROTOCOL_VERSION); @@ -457,19 +504,19 @@ bool CMasternodeBroadcast::SimpleCheck(int& nDos) } CScript pubkeyScript; - pubkeyScript = GetScriptForDestination(pubKeyCollateralAddress.GetID()); + pubkeyScript = GetScriptForDestination(keyIDCollateralAddress); if(pubkeyScript.size() != 25) { - LogPrintf("CMasternodeBroadcast::SimpleCheck -- pubKeyCollateralAddress has the wrong size\n"); + LogPrintf("CMasternodeBroadcast::SimpleCheck -- keyIDCollateralAddress has the wrong size\n"); nDos = 100; return false; } CScript pubkeyScript2; - pubkeyScript2 = GetScriptForDestination(pubKeyMasternode.GetID()); + pubkeyScript2 = GetScriptForDestination(legacyKeyIDOperator); if(pubkeyScript2.size() != 25) { - LogPrintf("CMasternodeBroadcast::SimpleCheck -- pubKeyMasternode has the wrong size\n"); + LogPrintf("CMasternodeBroadcast::SimpleCheck -- keyIDOperator has the wrong size\n"); nDos = 100; return false; } @@ -511,8 +558,8 @@ bool CMasternodeBroadcast::Update(CMasternode* pmn, int& nDos, CConnman& connman } // IsVnAssociatedWithPubkey is validated once in CheckOutpoint, after that they just need to match - if(pmn->pubKeyCollateralAddress != pubKeyCollateralAddress) { - LogPrintf("CMasternodeBroadcast::Update -- Got mismatched pubKeyCollateralAddress and outpoint\n"); + if(pmn->keyIDCollateralAddress != keyIDCollateralAddress) { + LogPrintf("CMasternodeBroadcast::Update -- Got mismatched keyIDCollateralAddress and outpoint\n"); nDos = 33; return false; } @@ -523,7 +570,7 @@ bool CMasternodeBroadcast::Update(CMasternode* pmn, int& nDos, CConnman& connman } // if ther was no masternode broadcast recently or if it matches our Masternode privkey... - if(!pmn->IsBroadcastedWithin(MASTERNODE_MIN_MNB_SECONDS) || (fMasternodeMode && pubKeyMasternode == activeMasternode.pubKeyMasternode)) { + if(!pmn->IsBroadcastedWithin(MASTERNODE_MIN_MNB_SECONDS) || (fMasternodeMode && legacyKeyIDOperator == activeMasternodeInfo.legacyKeyIDOperator)) { // take the newest entry LogPrintf("CMasternodeBroadcast::Update -- Got UPDATED Masternode entry: addr=%s\n", addr.ToString()); if(pmn->UpdateFromNewBroadcast(*this, connman)) { @@ -540,19 +587,24 @@ bool CMasternodeBroadcast::CheckOutpoint(int& nDos) { // we are a masternode with the same outpoint (i.e. already activated) and this mnb is ours (matches our Masternode privkey) // so nothing to do here for us - if(fMasternodeMode && outpoint == activeMasternode.outpoint && pubKeyMasternode == activeMasternode.pubKeyMasternode) { + if(fMasternodeMode && outpoint == activeMasternodeInfo.outpoint && legacyKeyIDOperator == activeMasternodeInfo.legacyKeyIDOperator) { return false; } AssertLockHeld(cs_main); int nHeight; - CollateralStatus err = CheckCollateral(outpoint, pubKeyCollateralAddress, nHeight); + CollateralStatus err = CheckCollateral(outpoint, keyIDCollateralAddress, nHeight); if (err == COLLATERAL_UTXO_NOT_FOUND) { LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Failed to find Masternode UTXO, masternode=%s\n", outpoint.ToStringShort()); return false; } + if (err == COLLATERAL_UTXO_NOT_PROTX) { + LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should be a ProTx, masternode=%s\n", outpoint.ToStringShort()); + return false; + } + if (err == COLLATERAL_INVALID_AMOUNT) { LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should have 2500 ABS, masternode=%s\n", outpoint.ToStringShort()); nDos = 33; @@ -560,7 +612,7 @@ bool CMasternodeBroadcast::CheckOutpoint(int& nDos) } if(err == COLLATERAL_INVALID_PUBKEY) { - LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should match pubKeyCollateralAddress, masternode=%s\n", outpoint.ToStringShort()); + LogPrint("masternode", "CMasternodeBroadcast::CheckOutpoint -- Masternode UTXO should match keyIDCollateralAddress, masternode=%s\n", outpoint.ToStringShort()); nDos = 33; return false; } @@ -634,21 +686,21 @@ bool CMasternodeBroadcast::Sign(const CKey& keyCollateralAddress) return false; } - if (!CHashSigner::VerifyHash(hash, pubKeyCollateralAddress, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyIDCollateralAddress, vchSig, strError)) { LogPrintf("CMasternodeBroadcast::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } } else { - std::string strMessage = addr.ToString(false) + boost::lexical_cast(sigTime) + - pubKeyCollateralAddress.GetID().ToString() + pubKeyMasternode.GetID().ToString() + - boost::lexical_cast(nProtocolVersion); + std::string strMessage = addr.ToString(false) + std::to_string(sigTime) + + keyIDCollateralAddress.ToString() + legacyKeyIDOperator.ToString() + + std::to_string(nProtocolVersion); if (!CMessageSigner::SignMessage(strMessage, vchSig, keyCollateralAddress)) { LogPrintf("CMasternodeBroadcast::Sign -- SignMessage() failed\n"); return false; } - if (!CMessageSigner::VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(keyIDCollateralAddress, vchSig, strMessage, strError)) { LogPrintf("CMasternodeBroadcast::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -665,13 +717,13 @@ bool CMasternodeBroadcast::CheckSignature(int& nDos) const if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, pubKeyCollateralAddress, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyIDCollateralAddress, vchSig, strError)) { // maybe it's in old format - std::string strMessage = addr.ToString(false) + boost::lexical_cast(sigTime) + - pubKeyCollateralAddress.GetID().ToString() + pubKeyMasternode.GetID().ToString() + - boost::lexical_cast(nProtocolVersion); + std::string strMessage = addr.ToString(false) + std::to_string(sigTime) + + keyIDCollateralAddress.ToString() + legacyKeyIDOperator.ToString() + + std::to_string(nProtocolVersion); - if (!CMessageSigner::VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)){ + if (!CMessageSigner::VerifyMessage(keyIDCollateralAddress, vchSig, strMessage, strError)){ // nope, not in old format either LogPrintf("CMasternodeBroadcast::CheckSignature -- Got bad Masternode announce signature, error: %s\n", strError); nDos = 100; @@ -679,11 +731,11 @@ bool CMasternodeBroadcast::CheckSignature(int& nDos) const } } } else { - std::string strMessage = addr.ToString(false) + boost::lexical_cast(sigTime) + - pubKeyCollateralAddress.GetID().ToString() + pubKeyMasternode.GetID().ToString() + - boost::lexical_cast(nProtocolVersion); + std::string strMessage = addr.ToString(false) + std::to_string(sigTime) + + keyIDCollateralAddress.ToString() + legacyKeyIDOperator.ToString() + + std::to_string(nProtocolVersion); - if (!CMessageSigner::VerifyMessage(pubKeyCollateralAddress, vchSig, strMessage, strError)){ + if(!CMessageSigner::VerifyMessage(keyIDCollateralAddress, vchSig, strMessage, strError)){ LogPrintf("CMasternodeBroadcast::CheckSignature -- Got bad Masternode announce signature, error: %s\n", strError); nDos = 100; return false; @@ -741,7 +793,7 @@ CMasternodePing::CMasternodePing(const COutPoint& outpoint) nDaemonVersion = CLIENT_VERSION; } -bool CMasternodePing::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMasternode) +bool CMasternodePing::Sign(const CKey& keyMasternode, const CKeyID& keyIDOperator) { std::string strError; @@ -755,20 +807,20 @@ bool CMasternodePing::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMaste return false; } - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyIDOperator, vchSig, strError)) { LogPrintf("CMasternodePing::Sign -- VerifyHash() failed, error: %s\n", strError); return false; } } else { std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() + - boost::lexical_cast(sigTime); + std::to_string(sigTime); if (!CMessageSigner::SignMessage(strMessage, vchSig, keyMasternode)) { LogPrintf("CMasternodePing::Sign -- SignMessage() failed\n"); return false; } - if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { LogPrintf("CMasternodePing::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -777,7 +829,7 @@ bool CMasternodePing::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMaste return true; } -bool CMasternodePing::CheckSignature(const CPubKey& pubKeyMasternode, int &nDos) const +bool CMasternodePing::CheckSignature(CKeyID& keyIDOperator, int &nDos) const { std::string strError = ""; nDos = 0; @@ -785,11 +837,11 @@ bool CMasternodePing::CheckSignature(const CPubKey& pubKeyMasternode, int &nDos) if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyIDOperator, vchSig, strError)) { std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() + - boost::lexical_cast(sigTime); + std::to_string(sigTime); - if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { LogPrintf("CMasternodePing::CheckSignature -- Got bad Masternode ping signature, masternode=%s, error: %s\n", masternodeOutpoint.ToStringShort(), strError); nDos = 33; return false; @@ -797,9 +849,9 @@ bool CMasternodePing::CheckSignature(const CPubKey& pubKeyMasternode, int &nDos) } } else { std::string strMessage = CTxIn(masternodeOutpoint).ToString() + blockHash.ToString() + - boost::lexical_cast(sigTime); + std::to_string(sigTime); - if (!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if (!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { LogPrintf("CMasternodePing::CheckSignature -- Got bad Masternode ping signature, masternode=%s, error: %s\n", masternodeOutpoint.ToStringShort(), strError); nDos = 33; return false; @@ -846,7 +898,7 @@ bool CMasternodePing::CheckAndUpdate(CMasternode* pmn, bool fFromNewBroadcast, i return false; } - if (pmn == NULL) { + if (pmn == nullptr) { LogPrint("masternode", "CMasternodePing::CheckAndUpdate -- Couldn't find Masternode entry, masternode=%s\n", masternodeOutpoint.ToStringShort()); return false; } @@ -883,7 +935,7 @@ bool CMasternodePing::CheckAndUpdate(CMasternode* pmn, bool fFromNewBroadcast, i return false; } - if (!CheckSignature(pmn->pubKeyMasternode, nDos)) return false; + if (!CheckSignature(pmn->legacyKeyIDOperator, nDos)) return false; // so, ping seems to be ok @@ -929,22 +981,28 @@ void CMasternodePing::Relay(CConnman& connman) connman.RelayInv(inv); } +std::string CMasternodePing::GetSentinelString() const +{ + return nSentinelVersion > DEFAULT_SENTINEL_VERSION ? SafeIntVersionToString(nSentinelVersion) : "Unknown"; +} + +std::string CMasternodePing::GetDaemonString() const +{ + return nDaemonVersion > DEFAULT_DAEMON_VERSION ? FormatVersion(nDaemonVersion) : "Unknown"; +} + void CMasternode::AddGovernanceVote(uint256 nGovernanceObjectHash) { - if(mapGovernanceObjectsVotedOn.count(nGovernanceObjectHash)) { - mapGovernanceObjectsVotedOn[nGovernanceObjectHash]++; - } else { - mapGovernanceObjectsVotedOn.insert(std::make_pair(nGovernanceObjectHash, 1)); - } + // Insert a zero value, or not. Then increment the value regardless. This + // ensures the value is in the map. + const auto& pair = mapGovernanceObjectsVotedOn.emplace(nGovernanceObjectHash, 0); + pair.first->second++; } void CMasternode::RemoveGovernanceObject(uint256 nGovernanceObjectHash) { - std::map::iterator it = mapGovernanceObjectsVotedOn.find(nGovernanceObjectHash); - if(it == mapGovernanceObjectsVotedOn.end()) { - return; - } - mapGovernanceObjectsVotedOn.erase(it); + // Whether or not the govobj hash exists in the map first is irrelevant. + mapGovernanceObjectsVotedOn.erase(nGovernanceObjectHash); } /** @@ -955,15 +1013,7 @@ void CMasternode::RemoveGovernanceObject(uint256 nGovernanceObjectHash) */ void CMasternode::FlagGovernanceItemsAsDirty() { - std::vector vecDirty; - { - std::map::iterator it = mapGovernanceObjectsVotedOn.begin(); - while(it != mapGovernanceObjectsVotedOn.end()) { - vecDirty.push_back(it->first); - ++it; - } - } - for(size_t i = 0; i < vecDirty.size(); ++i) { - mnodeman.AddDirtyGovernanceObjectHash(vecDirty[i]); + for (auto& govObjHashPair : mapGovernanceObjectsVotedOn) { + mnodeman.AddDirtyGovernanceObjectHash(govObjHashPair.first); } } diff --git a/src/masternode.h b/src/masternode.h index 915400bebcca..019f7acd2e3a 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2019 The Dash Core developers +// Copyright (c) 2018-2019 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -9,6 +9,9 @@ #include "key.h" #include "validation.h" #include "spork.h" +#include "bls/bls.h" + +#include "evo/deterministicmns.h" class CMasternode; class CMasternodeBroadcast; @@ -53,43 +56,15 @@ class CMasternodePing template inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (nVersion == 70209 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin{}; - if (ser_action.ForRead()) { - READWRITE(txin); - masternodeOutpoint = txin.prevout; - } else { - txin = CTxIn(masternodeOutpoint); - READWRITE(txin); - } - } else { - // using new format directly - READWRITE(masternodeOutpoint); - } + READWRITE(masternodeOutpoint); READWRITE(blockHash); READWRITE(sigTime); if (!(s.GetType() & SER_GETHASH)) { READWRITE(vchSig); } - if(ser_action.ForRead() && s.size() == 0) { - // TODO: drop this after migration to 70209 - fSentinelIsCurrent = false; - nSentinelVersion = DEFAULT_SENTINEL_VERSION; - nDaemonVersion = DEFAULT_DAEMON_VERSION; - return; - } READWRITE(fSentinelIsCurrent); READWRITE(nSentinelVersion); - if(ser_action.ForRead() && s.size() == 0) { - // TODO: drop this after migration to 70210 - nDaemonVersion = DEFAULT_DAEMON_VERSION; - return; - } - if (!(nVersion == 70209 && (s.GetType() & SER_NETWORK))) { - READWRITE(nDaemonVersion); - } + READWRITE(nDaemonVersion); } uint256 GetHash() const; @@ -97,12 +72,15 @@ class CMasternodePing bool IsExpired() const { return GetAdjustedTime() - sigTime > MASTERNODE_NEW_START_REQUIRED_SECONDS; } - bool Sign(const CKey& keyMasternode, const CPubKey& pubKeyMasternode); - bool CheckSignature(const CPubKey& pubKeyMasternode, int &nDos) const; + bool Sign(const CKey& keyMasternode, const CKeyID& keyIDOperator); + bool CheckSignature(CKeyID& keyIDOperator, int &nDos) const; bool SimpleCheck(int& nDos); bool CheckAndUpdate(CMasternode* pmn, bool fFromNewBroadcast, int& nDos, CConnman& connman); void Relay(CConnman& connman); + std::string GetSentinelString() const; + std::string GetDaemonString() const; + explicit operator bool() const; }; @@ -129,12 +107,21 @@ struct masternode_info_t masternode_info_t(int activeState, int protoVer, int64_t sTime) : nActiveState{activeState}, nProtocolVersion{protoVer}, sigTime{sTime} {} + // only called when the network is in legacy MN list mode masternode_info_t(int activeState, int protoVer, int64_t sTime, COutPoint const& outpnt, CService const& addr, CPubKey const& pkCollAddr, CPubKey const& pkMN) : nActiveState{activeState}, nProtocolVersion{protoVer}, sigTime{sTime}, outpoint{outpnt}, addr{addr}, - pubKeyCollateralAddress{pkCollAddr}, pubKeyMasternode{pkMN} {} + pubKeyCollateralAddress{pkCollAddr}, pubKeyMasternode{pkMN}, keyIDCollateralAddress{pkCollAddr.GetID()}, keyIDOwner{pkMN.GetID()}, legacyKeyIDOperator{pkMN.GetID()}, keyIDVoting{pkMN.GetID()} {} + + // only called when the network is in deterministic MN list mode + masternode_info_t(int activeState, int protoVer, int64_t sTime, + COutPoint const& outpnt, CService const& addr, + CKeyID const& pkCollAddr, CKeyID const& pkOwner, CBLSPublicKey const& pkOperator, CKeyID const& pkVoting) : + nActiveState{activeState}, nProtocolVersion{protoVer}, sigTime{sTime}, + outpoint{outpnt}, addr{addr}, + pubKeyCollateralAddress{}, pubKeyMasternode{}, keyIDCollateralAddress{pkCollAddr}, keyIDOwner{pkOwner}, blsPubKeyOperator{pkOperator}, keyIDVoting{pkVoting} {} int nActiveState = 0; int nProtocolVersion = 0; @@ -142,8 +129,13 @@ struct masternode_info_t COutPoint outpoint{}; CService addr{}; - CPubKey pubKeyCollateralAddress{}; - CPubKey pubKeyMasternode{}; + CPubKey pubKeyCollateralAddress{}; // this will be invalid/unset when the network switches to deterministic MNs (luckely it's only important for the broadcast hash) + CPubKey pubKeyMasternode{}; // this will be invalid/unset when the network switches to deterministic MNs (luckely it's only important for the broadcast hash) + CKeyID keyIDCollateralAddress{}; + CKeyID keyIDOwner{}; + CKeyID legacyKeyIDOperator{}; + CBLSPublicKey blsPubKeyOperator; + CKeyID keyIDVoting{}; int64_t nLastDsq = 0; //the dsq count from the last dsq broadcast of this node int64_t nTimeLastChecked = 0; @@ -178,7 +170,8 @@ class CMasternode : public masternode_info_t COLLATERAL_OK, COLLATERAL_UTXO_NOT_FOUND, COLLATERAL_INVALID_AMOUNT, - COLLATERAL_INVALID_PUBKEY + COLLATERAL_INVALID_PUBKEY, + COLLATERAL_UTXO_NOT_PROTX, }; @@ -199,30 +192,22 @@ class CMasternode : public masternode_info_t CMasternode(const CMasternode& other); CMasternode(const CMasternodeBroadcast& mnb); CMasternode(CService addrNew, COutPoint outpointNew, CPubKey pubKeyCollateralAddressNew, CPubKey pubKeyMasternodeNew, int nProtocolVersionIn); + CMasternode(const uint256 &proTxHash, const CDeterministicMNCPtr& dmn); ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { LOCK(cs); - int nVersion = s.GetVersion(); - if (nVersion == 70209 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin{}; - if (ser_action.ForRead()) { - READWRITE(txin); - outpoint = txin.prevout; - } else { - txin = CTxIn(outpoint); - READWRITE(txin); - } - } else { - // using new format directly - READWRITE(outpoint); - } + READWRITE(outpoint); READWRITE(addr); READWRITE(pubKeyCollateralAddress); READWRITE(pubKeyMasternode); + READWRITE(keyIDCollateralAddress); + READWRITE(keyIDOwner); + READWRITE(legacyKeyIDOperator); + READWRITE(blsPubKeyOperator); + READWRITE(keyIDVoting); READWRITE(lastPing); READWRITE(vchSig); READWRITE(sigTime); @@ -245,8 +230,8 @@ class CMasternode : public masternode_info_t bool UpdateFromNewBroadcast(CMasternodeBroadcast& mnb, CConnman& connman); - static CollateralStatus CheckCollateral(const COutPoint& outpoint, const CPubKey& pubkey); - static CollateralStatus CheckCollateral(const COutPoint& outpoint, const CPubKey& pubkey, int& nHeightRet); + static CollateralStatus CheckCollateral(const COutPoint& outpoint, const CKeyID& keyID); + static CollateralStatus CheckCollateral(const COutPoint& outpoint, const CKeyID& keyID, int& nHeightRet); void Check(bool fForce = false); bool IsBroadcastedWithin(int nSeconds) { return GetAdjustedTime() - sigTime < nSeconds; } @@ -362,21 +347,7 @@ class CMasternodeBroadcast : public CMasternode template inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (nVersion == 70209 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin{}; - if (ser_action.ForRead()) { - READWRITE(txin); - outpoint = txin.prevout; - } else { - txin = CTxIn(outpoint); - READWRITE(txin); - } - } else { - // using new format directly - READWRITE(outpoint); - } + READWRITE(outpoint); READWRITE(addr); READWRITE(pubKeyCollateralAddress); READWRITE(pubKeyMasternode); @@ -388,6 +359,13 @@ class CMasternodeBroadcast : public CMasternode if (!(s.GetType() & SER_GETHASH)) { READWRITE(lastPing); } + + if (ser_action.ForRead()) { + keyIDCollateralAddress = pubKeyCollateralAddress.GetID(); + keyIDOwner = pubKeyMasternode.GetID(); + legacyKeyIDOperator = pubKeyMasternode.GetID(); + keyIDVoting = pubKeyMasternode.GetID(); + } } uint256 GetHash() const; @@ -429,27 +407,8 @@ class CMasternodeVerification template inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (nVersion == 70209 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin1{}; - CTxIn txin2{}; - if (ser_action.ForRead()) { - READWRITE(txin1); - READWRITE(txin2); - masternodeOutpoint1 = txin1.prevout; - masternodeOutpoint2 = txin2.prevout; - } else { - txin1 = CTxIn(masternodeOutpoint1); - txin2 = CTxIn(masternodeOutpoint2); - READWRITE(txin1); - READWRITE(txin2); - } - } else { - // using new format directly - READWRITE(masternodeOutpoint1); - READWRITE(masternodeOutpoint2); - } + READWRITE(masternodeOutpoint1); + READWRITE(masternodeOutpoint2); READWRITE(addr); READWRITE(nonce); READWRITE(nBlockHeight); diff --git a/src/masternodeconfig.cpp b/src/masternodeconfig.cpp index 31d4cacc12d4..31182359034e 100644 --- a/src/masternodeconfig.cpp +++ b/src/masternodeconfig.cpp @@ -21,7 +21,7 @@ bool CMasternodeConfig::read(std::string& strErrRet) { if (!streamConfig.good()) { FILE* configFile = fopen(pathMasternodeConfigFile.string().c_str(), "a"); - if (configFile != NULL) { + if (configFile != nullptr) { std::string strHeader = "# Masternode config file\n" "# Format: alias IP:port masternodeprivkey collateral_output_txid collateral_output_index\n" "# Example: mn1 127.0.0.2:18888 43HaYBVUCYjEMeeH1Y4sBGLALQZE1Yc1K64xiqgX37tGBDQL8Xg 2bcd3c84c84f87eaa86e4e56834c92927a07f9e18718810b92e0d0324456a67c 0\n"; diff --git a/src/masternodeconfig.h b/src/masternodeconfig.h index fb4a404b775a..07a85682b971 100644 --- a/src/masternodeconfig.h +++ b/src/masternodeconfig.h @@ -1,6 +1,6 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -37,41 +37,21 @@ class CMasternodeConfig return alias; } - void setAlias(const std::string& alias) { - this->alias = alias; - } - const std::string& getOutputIndex() const { return outputIndex; } - void setOutputIndex(const std::string& outputIndex) { - this->outputIndex = outputIndex; - } - const std::string& getPrivKey() const { return privKey; } - void setPrivKey(const std::string& privKey) { - this->privKey = privKey; - } - const std::string& getTxHash() const { return txHash; } - void setTxHash(const std::string& txHash) { - this->txHash = txHash; - } - const std::string& getIp() const { return ip; } - - void setIp(const std::string& ip) { - this->ip = ip; - } }; CMasternodeConfig() { diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 7c06d5b90f8f..3eed0bfbc6d1 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -7,6 +7,7 @@ #include "addrman.h" #include "alert.h" #include "clientversion.h" +#include "init.h" #include "governance.h" #include "masternode-payments.h" #include "masternode-sync.h" @@ -22,10 +23,13 @@ #include "util.h" #include "warnings.h" +#include "evo/deterministicmns.h" +#include "evo/providertx.h" + /** Masternode manager */ CMasternodeMan mnodeman; -const std::string CMasternodeMan::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-8"; +const std::string CMasternodeMan::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-10"; const int CMasternodeMan::LAST_PAID_SCAN_BLOCKS = 50; struct CompareLastPaidBlock @@ -79,6 +83,9 @@ bool CMasternodeMan::Add(CMasternode &mn) { LOCK(cs); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return false; + if (Has(mn.outpoint)) return false; LogPrint("masternode", "CMasternodeMan::Add -- Adding new Masternode: addr=%s, %i now\n", mn.addr.ToString(), size() + 1); @@ -94,6 +101,9 @@ void CMasternodeMan::AskForMN(CNode* pnode, const COutPoint& outpoint, CConnman& CNetMsgMaker msgMaker(pnode->GetSendVersion()); LOCK(cs); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + CService addrSquashed = Params().AllowMultiplePorts() ? (CService)pnode->addr : CService(pnode->addr, 0); auto it1 = mWeAskedForMasternodeListEntry.find(outpoint); if (it1 != mWeAskedForMasternodeListEntry.end()) { @@ -115,11 +125,7 @@ void CMasternodeMan::AskForMN(CNode* pnode, const COutPoint& outpoint, CConnman& } mWeAskedForMasternodeListEntry[outpoint][addrSquashed] = GetTime() + DSEG_UPDATE_SECONDS; - if (pnode->GetSendVersion() == 70209) { - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSEG, CTxIn(outpoint))); - } else { - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSEG, outpoint)); - } + connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSEG, outpoint)); } bool CMasternodeMan::AllowMixing(const COutPoint &outpoint) @@ -151,6 +157,10 @@ bool CMasternodeMan::DisallowMixing(const COutPoint &outpoint) bool CMasternodeMan::PoSeBan(const COutPoint &outpoint) { LOCK(cs); + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return true; + CMasternode* pmn = Find(outpoint); if (!pmn) { return false; @@ -164,7 +174,8 @@ void CMasternodeMan::Check() { LOCK2(cs_main, cs); - LogPrint("masternode", "CMasternodeMan::Check -- nLastSentinelPingTime=%d, IsSentinelPingActive()=%d\n", nLastSentinelPingTime, IsSentinelPingActive()); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; for (auto& mnpair : mapMasternodes) { // NOTE: internally it checks only every MASTERNODE_CHECK_SECONDS seconds @@ -175,6 +186,9 @@ void CMasternodeMan::Check() void CMasternodeMan::CheckAndRemove(CConnman& connman) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + if(!masternodeSync.IsMasternodeListSynced()) return; LogPrintf("CMasternodeMan::CheckAndRemove\n"); @@ -254,7 +268,7 @@ void CMasternodeMan::CheckAndRemove(CConnman& connman) // mapSeenMasternodeBroadcast.erase(itMnbReplies->first); int nDos; itMnbReplies->second[0].fRecovery = true; - CheckMnbAndUpdateMasternodeList(NULL, itMnbReplies->second[0], nDos, connman); + CheckMnbAndUpdateMasternodeList(nullptr, itMnbReplies->second[0], nDos, connman); } LogPrint("masternode", "CMasternodeMan::CheckAndRemove -- removing mnb recovery reply, masternode=%s, size=%d\n", itMnbReplies->second[0].outpoint.ToStringShort(), (int)itMnbReplies->second.size()); mMnbRecoveryGoodReplies.erase(itMnbReplies++); @@ -357,6 +371,69 @@ void CMasternodeMan::CheckAndRemove(CConnman& connman) } } +void CMasternodeMan::AddDeterministicMasternodes() +{ + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + + bool added = false; + { + LOCK(cs); + unsigned int oldMnCount = mapMasternodes.size(); + + auto mnList = deterministicMNManager->GetListAtChainTip(); + mnList.ForEachMN(true, [this](const CDeterministicMNCPtr& dmn) { + // call Find() on each deterministic MN to force creation of CMasternode object + auto mn = Find(COutPoint(dmn->proTxHash, dmn->nCollateralIndex)); + assert(mn); + + // make sure we use the splitted keys from now on + mn->keyIDOwner = dmn->pdmnState->keyIDOwner; + mn->blsPubKeyOperator = dmn->pdmnState->pubKeyOperator; + mn->keyIDVoting = dmn->pdmnState->keyIDVoting; + mn->addr = dmn->pdmnState->addr; + mn->nProtocolVersion = dmn->pdmnState->nProtocolVersion; + + // If it appeared in the valid list, it is enabled no matter what + mn->nActiveState = CMasternode::MASTERNODE_ENABLED; + }); + + added = oldMnCount != mapMasternodes.size(); + } + + if (added) { + NotifyMasternodeUpdates(*g_connman, true, false); + } +} + +void CMasternodeMan::RemoveNonDeterministicMasternodes() +{ + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + + bool erased = false; + { + LOCK(cs); + std::set mnSet; + auto mnList = deterministicMNManager->GetListAtChainTip(); + mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) { + mnSet.insert(COutPoint(dmn->proTxHash, dmn->nCollateralIndex)); + }); + auto it = mapMasternodes.begin(); + while (it != mapMasternodes.end()) { + if (!mnSet.count(it->second.outpoint)) { + mapMasternodes.erase(it++); + erased = true; + } else { + ++it; + } + } + } + if (erased) { + NotifyMasternodeUpdates(*g_connman, false, true); + } +} + void CMasternodeMan::Clear() { LOCK(cs); @@ -373,20 +450,33 @@ void CMasternodeMan::Clear() int CMasternodeMan::CountMasternodes(int nProtocolVersion) { LOCK(cs); + int nCount = 0; nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : nProtocolVersion; - for (const auto& mnpair : mapMasternodes) { - if(mnpair.second.nProtocolVersion < nProtocolVersion) continue; - nCount++; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + auto mnList = deterministicMNManager->GetListAtChainTip(); + mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) { + if (dmn->pdmnState->nProtocolVersion >= nProtocolVersion) { + nCount++; + } + }); + } else { + for (const auto& mnpair : mapMasternodes) { + if(mnpair.second.nProtocolVersion < nProtocolVersion) continue; + nCount++; + } } - return nCount; } int CMasternodeMan::CountEnabled(int nProtocolVersion) { LOCK(cs); + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return CountMasternodes(nProtocolVersion); + int nCount = 0; nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : nProtocolVersion; @@ -420,6 +510,9 @@ void CMasternodeMan::DsegUpdate(CNode* pnode, CConnman& connman) CNetMsgMaker msgMaker(pnode->GetSendVersion()); LOCK(cs); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + CService addrSquashed = Params().AllowMultiplePorts() ? (CService)pnode->addr : CService(pnode->addr, 0); if(Params().NetworkIDString() == CBaseChainParams::MAIN) { if(!(pnode->addr.IsRFC1918() || pnode->addr.IsLocal())) { @@ -431,11 +524,8 @@ void CMasternodeMan::DsegUpdate(CNode* pnode, CConnman& connman) } } - if (pnode->GetSendVersion() == 70209) { - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSEG, CTxIn())); - } else { - connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSEG, COutPoint())); - } + connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSEG, COutPoint())); + int64_t askAgain = GetTime() + DSEG_UPDATE_SECONDS; mWeAskedForMasternodeList[addrSquashed] = askAgain; @@ -445,52 +535,91 @@ void CMasternodeMan::DsegUpdate(CNode* pnode, CConnman& connman) CMasternode* CMasternodeMan::Find(const COutPoint &outpoint) { LOCK(cs); - auto it = mapMasternodes.find(outpoint); - return it == mapMasternodes.end() ? NULL : &(it->second); + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + // This code keeps compatibility to old code depending on the non-deterministic MN lists + // When deterministic MN lists get activated, we stop relying on the MNs we encountered due to MNBs and start + // using the MNs found in the deterministic MN manager. To keep compatibility, we create CMasternode entries + // for these and return them here. This is needed because we also need to track some data per MN that is not + // on-chain, like vote counts + + auto mnList = deterministicMNManager->GetListAtChainTip(); + if (!mnList.IsMNValid(outpoint.hash)) { + return nullptr; + } + auto dmn = mnList.GetMN(outpoint.hash); + if (!dmn) { + return nullptr; + } + + auto it = mapMasternodes.find(outpoint); + if (it != mapMasternodes.end()) { + return &(it->second); + } else { + // MN is not in mapMasternodes but in the deterministic list. Create an entry in mapMasternodes for compatibility with legacy code + CMasternode mn(outpoint.hash, dmn); + it = mapMasternodes.emplace(outpoint, mn).first; + return &(it->second); + } + } else { + auto it = mapMasternodes.find(outpoint); + return it == mapMasternodes.end() ? nullptr : &(it->second); + } } bool CMasternodeMan::Get(const COutPoint& outpoint, CMasternode& masternodeRet) { // Theses mutexes are recursive so double locking by the same thread is safe. LOCK(cs); - auto it = mapMasternodes.find(outpoint); - if (it == mapMasternodes.end()) { + CMasternode* mn = Find(outpoint); + if (!mn) return false; - } - - masternodeRet = it->second; + masternodeRet = *mn; return true; } +bool CMasternodeMan::GetMasternodeInfo(const uint256& proTxHash, masternode_info_t& mnInfoRet) +{ + auto dmn = deterministicMNManager->GetListAtChainTip().GetValidMN(proTxHash); + if (!dmn) + return false; + return GetMasternodeInfo(COutPoint(proTxHash, dmn->nCollateralIndex), mnInfoRet); +} + bool CMasternodeMan::GetMasternodeInfo(const COutPoint& outpoint, masternode_info_t& mnInfoRet) { LOCK(cs); - auto it = mapMasternodes.find(outpoint); - if (it == mapMasternodes.end()) { + CMasternode* mn = Find(outpoint); + if (!mn) return false; - } - mnInfoRet = it->second.GetInfo(); + mnInfoRet = mn->GetInfo(); return true; } -bool CMasternodeMan::GetMasternodeInfo(const CPubKey& pubKeyMasternode, masternode_info_t& mnInfoRet) -{ +bool CMasternodeMan::GetMasternodeInfo(const CKeyID& keyIDOperator, masternode_info_t& mnInfoRet) { LOCK(cs); - for (const auto& mnpair : mapMasternodes) { - if (mnpair.second.pubKeyMasternode == pubKeyMasternode) { - mnInfoRet = mnpair.second.GetInfo(); - return true; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + return false; + } else { + for (const auto& mnpair : mapMasternodes) { + if (mnpair.second.legacyKeyIDOperator == keyIDOperator) { + mnInfoRet = mnpair.second.GetInfo(); + return true; + } } + return false; } - return false; } bool CMasternodeMan::GetMasternodeInfo(const CScript& payee, masternode_info_t& mnInfoRet) { + CTxDestination dest; + if (!ExtractDestination(payee, dest) || !boost::get(&dest)) + return false; + CKeyID keyId = *boost::get(&dest); LOCK(cs); for (const auto& mnpair : mapMasternodes) { - CScript scriptCollateralAddress = GetScriptForDestination(mnpair.second.pubKeyCollateralAddress.GetID()); - if (scriptCollateralAddress == payee) { + if (mnpair.second.keyIDCollateralAddress == keyId) { mnInfoRet = mnpair.second.GetInfo(); return true; } @@ -501,7 +630,11 @@ bool CMasternodeMan::GetMasternodeInfo(const CScript& payee, masternode_info_t& bool CMasternodeMan::Has(const COutPoint& outpoint) { LOCK(cs); - return mapMasternodes.find(outpoint) != mapMasternodes.end(); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + return deterministicMNManager->HasValidMNAtChainTip(outpoint.hash); + } else { + return mapMasternodes.find(outpoint) != mapMasternodes.end(); + } } // @@ -514,6 +647,10 @@ bool CMasternodeMan::GetNextMasternodeInQueueForPayment(bool fFilterSigTime, int bool CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight, bool fFilterSigTime, int& nCountRet, masternode_info_t& mnInfoRet) { + if (deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) { + return false; + } + mnInfoRet = masternode_info_t(); nCountRet = 0; @@ -572,7 +709,7 @@ bool CMasternodeMan::GetNextMasternodeInQueueForPayment(int nBlockHeight, bool f int nTenthNetwork = nMnCount/10; int nCountTenth = 0; arith_uint256 nHighest = 0; - const CMasternode *pBestMasternode = NULL; + const CMasternode *pBestMasternode = nullptr; for (const auto& s : vecMasternodeLastPaid) { arith_uint256 nScore = s.second->CalculateScore(blockHash); if(nScore > nHighest){ @@ -622,6 +759,8 @@ masternode_info_t CMasternodeMan::FindRandomNotInVec(const std::vectorIsDeterministicMNsSporkActive() && !deterministicMNManager->HasValidMNAtChainTip(pmn->outpoint.hash)) + continue; // found the one not in vecToExclude LogPrint("masternode", "CMasternodeMan::FindRandomNotInVec -- found, masternode=%s\n", pmn->outpoint.ToStringShort()); return pmn->GetInfo(); @@ -631,6 +770,23 @@ masternode_info_t CMasternodeMan::FindRandomNotInVec(const std::vector CMasternodeMan::GetFullMasternodeMap() +{ + LOCK(cs); + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + std::map result; + for (const auto &p : mapMasternodes) { + if (deterministicMNManager->HasValidMNAtChainTip(p.first.hash)) { + result.emplace(p.first, p.second); + } + } + return result; + } else { + return mapMasternodes; + } +} + bool CMasternodeMan::GetMasternodeScores(const uint256& nBlockHash, CMasternodeMan::score_pair_vec_t& vecMasternodeScoresRet, int nMinProtocol) { vecMasternodeScoresRet.clear(); @@ -645,6 +801,9 @@ bool CMasternodeMan::GetMasternodeScores(const uint256& nBlockHash, CMasternodeM // calculate scores for (const auto& mnpair : mapMasternodes) { + if (deterministicMNManager->IsDeterministicMNsSporkActive() && !deterministicMNManager->HasValidMNAtChainTip(mnpair.second.outpoint.hash)) + continue; + if (mnpair.second.nProtocolVersion >= nMinProtocol) { vecMasternodeScoresRet.push_back(std::make_pair(mnpair.second.CalculateScore(nBlockHash), &mnpair.second)); } @@ -720,11 +879,22 @@ void CMasternodeMan::ProcessMasternodeConnections(CConnman& connman) //we don't care about this for regtest if(Params().NetworkIDString() == CBaseChainParams::REGTEST) return; - connman.ForEachNode(CConnman::AllNodes, [](CNode* pnode) { + std::vector vecMnInfo; // will be empty when no wallet #ifdef ENABLE_WALLET - if(pnode->fMasternode && !privateSendClient.IsMixingMasternode(pnode)) { -#else - if(pnode->fMasternode) { + privateSendClient.GetMixingMasternodesInfo(vecMnInfo); +#endif // ENABLE_WALLET + + connman.ForEachNode(CConnman::AllNodes, [&vecMnInfo](CNode* pnode) { + if (pnode->fMasternode) { +#ifdef ENABLE_WALLET + bool fFound = false; + for (const auto& mnInfo : vecMnInfo) { + if (pnode->addr == mnInfo.addr) { + fFound = true; + break; + } + } + if (fFound) return; // do NOT disconnect mixing masternodes #endif // ENABLE_WALLET LogPrintf("Closing Masternode connection: peer=%d, addr=%s\n", pnode->id, pnode->addr.ToString()); pnode->fDisconnect = true; @@ -735,6 +905,9 @@ void CMasternodeMan::ProcessMasternodeConnections(CConnman& connman) std::pair > CMasternodeMan::PopScheduledMnbRequestConnection() { LOCK(cs); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + return std::make_pair(CService(), std::set()); + } if(listScheduledMnbRequestConnections.empty()) { return std::make_pair(CService(), std::set()); } @@ -761,6 +934,9 @@ std::pair > CMasternodeMan::PopScheduledMnbRequestCo void CMasternodeMan::ProcessPendingMnbRequests(CConnman& connman) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + std::pair > p = PopScheduledMnbRequestConnection(); if (!(p.first == CService() || p.second.empty())) { if (connman.IsMasternodeOrDisconnectRequested(p.first)) return; @@ -773,14 +949,11 @@ void CMasternodeMan::ProcessPendingMnbRequests(CConnman& connman) bool fDone = connman.ForNode(itPendingMNB->first, [&](CNode* pnode) { // compile request vector std::vector vToFetch; - std::set& setHashes = itPendingMNB->second.second; - std::set::iterator it = setHashes.begin(); - while(it != setHashes.end()) { - if(*it != uint256()) { - vToFetch.push_back(CInv(MSG_MASTERNODE_ANNOUNCE, *it)); - LogPrint("masternode", "-- asking for mnb %s from addr=%s\n", it->ToString(), pnode->addr.ToString()); + for (auto& nHash : itPendingMNB->second.second) { + if(nHash != uint256()) { + vToFetch.push_back(CInv(MSG_MASTERNODE_ANNOUNCE, nHash)); + LogPrint("masternode", "-- asking for mnb %s from addr=%s\n", nHash.ToString(), pnode->addr.ToString()); } - ++it; } // ask for data @@ -799,11 +972,13 @@ void CMasternodeMan::ProcessPendingMnbRequests(CConnman& connman) ++itPendingMNB; } } - LogPrint("masternode", "%s -- mapPendingMNB size: %d\n", __func__, mapPendingMNB.size()); } void CMasternodeMan::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + if(fLiteMode) return; // disable all Absolute specific functionality if (strCommand == NetMsgType::MNANNOUNCE) { //Masternode Broadcast @@ -866,7 +1041,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, const std::string& strCommand, if(nDos > 0) { // if anything significant failed, mark that node Misbehaving(pfrom->GetId(), nDos); - } else if(pmn != NULL) { + } else if(pmn != nullptr) { // nothing significant failed, mn is a known one too return; } @@ -882,14 +1057,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, const std::string& strCommand, if (!masternodeSync.IsSynced()) return; COutPoint masternodeOutpoint; - - if (pfrom->nVersion == 70208) { - CTxIn vin; - vRecv >> vin; - masternodeOutpoint = vin.prevout; - } else { - vRecv >> masternodeOutpoint; - } + vRecv >> masternodeOutpoint; LogPrint("masternode", "DSEG -- Masternode list, masternode=%s\n", masternodeOutpoint.ToStringShort()); @@ -969,7 +1137,9 @@ void CMasternodeMan::SyncAll(CNode* pnode, CConnman& connman) LOCK(cs); for (const auto& mnpair : mapMasternodes) { - if (mnpair.second.addr.IsRFC1918() || mnpair.second.addr.IsLocal()) continue; // do not send local network masternode + if (Params().RequireRoutableExternalIP() && + (mnpair.second.addr.IsRFC1918() || mnpair.second.addr.IsLocal())) + continue; // do not send local network masternode // NOTE: send masternode regardless of its current state, the other node will need it to verify old votes. LogPrint("masternode", "CMasternodeMan::%s -- Sending Masternode entry: masternode=%s addr=%s\n", __func__, mnpair.first.ToStringShort(), mnpair.second.addr.ToString()); PushDsegInvs(pnode, mnpair.second); @@ -998,34 +1168,36 @@ void CMasternodeMan::PushDsegInvs(CNode* pnode, const CMasternode& mn) void CMasternodeMan::DoFullVerificationStep(CConnman& connman) { - if(activeMasternode.outpoint.IsNull()) return; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + + if(activeMasternodeInfo.outpoint.IsNull()) return; if(!masternodeSync.IsSynced()) return; rank_pair_vec_t vecMasternodeRanks; GetMasternodeRanks(vecMasternodeRanks, nCachedBlockHeight - 1, MIN_POSE_PROTO_VERSION); - LOCK(cs); + std::vector vAddr; - int nCount = 0; + { + LOCK(cs); int nMyRank = -1; int nRanksTotal = (int)vecMasternodeRanks.size(); // send verify requests only if we are in top MAX_POSE_RANK - rank_pair_vec_t::iterator it = vecMasternodeRanks.begin(); - while(it != vecMasternodeRanks.end()) { - if(it->first > MAX_POSE_RANK) { + for (auto& rankPair : vecMasternodeRanks) { + if(rankPair.first > MAX_POSE_RANK) { LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Must be in top %d to send verify request\n", (int)MAX_POSE_RANK); return; } - if(it->second.outpoint == activeMasternode.outpoint) { - nMyRank = it->first; + if(rankPair.second.outpoint == activeMasternodeInfo.outpoint) { + nMyRank = rankPair.first; LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Found self at rank %d/%d, verifying up to %d masternodes\n", nMyRank, nRanksTotal, (int)MAX_POSE_CONNECTIONS); break; } - ++it; } // edge case: list is too short and this masternode is not enabled @@ -1036,14 +1208,7 @@ void CMasternodeMan::DoFullVerificationStep(CConnman& connman) int nOffset = MAX_POSE_RANK + nMyRank - 1; if(nOffset >= (int)vecMasternodeRanks.size()) return; - std::vector vSortedByAddr; - for (const auto& mnpair : mapMasternodes) { - vSortedByAddr.push_back(&mnpair.second); - } - - sort(vSortedByAddr.begin(), vSortedByAddr.end(), CompareByAddr()); - - it = vecMasternodeRanks.begin() + nOffset; + auto it = vecMasternodeRanks.begin() + nOffset; while(it != vecMasternodeRanks.end()) { if(it->second.IsPoSeVerified() || it->second.IsPoSeBanned()) { LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Already %s%s%s masternode %s address %s, skipping...\n", @@ -1058,16 +1223,22 @@ void CMasternodeMan::DoFullVerificationStep(CConnman& connman) } LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Verifying masternode %s rank %d/%d address %s\n", it->second.outpoint.ToStringShort(), it->first, nRanksTotal, it->second.addr.ToString()); - if(SendVerifyRequest(CAddress(it->second.addr, NODE_NETWORK), vSortedByAddr, connman)) { - nCount++; - if(nCount >= MAX_POSE_CONNECTIONS) break; + CAddress addr = CAddress(it->second.addr, NODE_NETWORK); + if(CheckVerifyRequestAddr(addr, connman)) { + vAddr.push_back(addr); + if((int)vAddr.size() >= MAX_POSE_CONNECTIONS) break; } nOffset += MAX_POSE_CONNECTIONS; if(nOffset >= (int)vecMasternodeRanks.size()) break; it += MAX_POSE_CONNECTIONS; } + } // cs - LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Sent verification requests to %d masternodes\n", nCount); + for (const auto& addr : vAddr) { + PrepareVerifyRequest(addr, connman); + } + + LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Prepared verification requests for %d masternodes\n", vAddr.size()); } // This function tries to find masternodes with the same addr, @@ -1077,6 +1248,9 @@ void CMasternodeMan::DoFullVerificationStep(CConnman& connman) void CMasternodeMan::CheckSameAddr() { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + if(!masternodeSync.IsSynced() || mapMasternodes.empty()) return; std::vector vBan; @@ -1085,8 +1259,8 @@ void CMasternodeMan::CheckSameAddr() { LOCK(cs); - CMasternode* pprevMasternode = NULL; - CMasternode* pverifiedMasternode = NULL; + CMasternode* pprevMasternode = nullptr; + CMasternode* pverifiedMasternode = nullptr; for (auto& mnpair : mapMasternodes) { vSortedByAddr.push_back(&mnpair.second); @@ -1100,7 +1274,7 @@ void CMasternodeMan::CheckSameAddr() // initial step if(!pprevMasternode) { pprevMasternode = pmn; - pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : NULL; + pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : nullptr; continue; } // second+ step @@ -1115,7 +1289,7 @@ void CMasternodeMan::CheckSameAddr() pverifiedMasternode = pmn; } } else { - pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : NULL; + pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : nullptr; } pprevMasternode = pmn; } @@ -1128,29 +1302,37 @@ void CMasternodeMan::CheckSameAddr() } } -bool CMasternodeMan::SendVerifyRequest(const CAddress& addr, const std::vector& vSortedByAddr, CConnman& connman) +bool CMasternodeMan::CheckVerifyRequestAddr(const CAddress& addr, CConnman& connman) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return false; + if(netfulfilledman.HasFulfilledRequest(addr, strprintf("%s", NetMsgType::MNVERIFY)+"-request")) { // we already asked for verification, not a good idea to do this too often, skip it - LogPrint("masternode", "CMasternodeMan::SendVerifyRequest -- too many requests, skipping... addr=%s\n", addr.ToString()); + LogPrint("masternode", "CMasternodeMan::%s -- too many requests, skipping... addr=%s\n", __func__, addr.ToString()); return false; } - if (connman.IsMasternodeOrDisconnectRequested(addr)) return false; + return !connman.IsMasternodeOrDisconnectRequested(addr); +} +void CMasternodeMan::PrepareVerifyRequest(const CAddress& addr, CConnman& connman) +{ connman.AddPendingMasternode(addr); // use random nonce, store it and require node to reply with correct one later CMasternodeVerification mnv(addr, GetRandInt(999999), nCachedBlockHeight - 1); LOCK(cs_mapPendingMNV); mapPendingMNV.insert(std::make_pair(addr, std::make_pair(GetTime(), mnv))); - LogPrintf("CMasternodeMan::SendVerifyRequest -- verifying node using nonce %d addr=%s\n", mnv.nonce, addr.ToString()); - return true; + LogPrintf("CMasternodeMan::%s -- verifying node using nonce %d addr=%s\n", __func__, mnv.nonce, addr.ToString()); } void CMasternodeMan::ProcessPendingMnvRequests(CConnman& connman) { LOCK(cs_mapPendingMNV); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + std::map >::iterator itPendingMNV = mapPendingMNV.begin(); while (itPendingMNV != mapPendingMNV.end()) { @@ -1174,13 +1356,15 @@ void CMasternodeMan::ProcessPendingMnvRequests(CConnman& connman) ++itPendingMNV; } } - LogPrint("masternode", "%s -- mapPendingMNV size: %d\n", __func__, mapPendingMNV.size()); } void CMasternodeMan::SendVerifyReply(CNode* pnode, CMasternodeVerification& mnv, CConnman& connman) { AssertLockHeld(cs_main); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + // only masternodes can sign this, why would someone ask regular node? if(!fMasternodeMode) { // do not ban, malicious node might be using my IP @@ -1206,24 +1390,24 @@ void CMasternodeMan::SendVerifyReply(CNode* pnode, CMasternodeVerification& mnv, if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = mnv.GetSignatureHash1(blockHash); - if(!CHashSigner::SignHash(hash, activeMasternode.keyMasternode, mnv.vchSig1)) { + if(!CHashSigner::SignHash(hash, activeMasternodeInfo.legacyKeyOperator, mnv.vchSig1)) { LogPrintf("CMasternodeMan::SendVerifyReply -- SignHash() failed\n"); return; } - if (!CHashSigner::VerifyHash(hash, activeMasternode.pubKeyMasternode, mnv.vchSig1, strError)) { + if (!CHashSigner::VerifyHash(hash, activeMasternodeInfo.legacyKeyIDOperator, mnv.vchSig1, strError)) { LogPrintf("CMasternodeMan::SendVerifyReply -- VerifyHash() failed, error: %s\n", strError); return; } } else { - std::string strMessage = strprintf("%s%d%s", activeMasternode.service.ToString(false), mnv.nonce, blockHash.ToString()); + std::string strMessage = strprintf("%s%d%s", activeMasternodeInfo.service.ToString(false), mnv.nonce, blockHash.ToString()); - if(!CMessageSigner::SignMessage(strMessage, mnv.vchSig1, activeMasternode.keyMasternode)) { + if(!CMessageSigner::SignMessage(strMessage, mnv.vchSig1, activeMasternodeInfo.legacyKeyOperator)) { LogPrintf("MasternodeMan::SendVerifyReply -- SignMessage() failed\n"); return; } - if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, mnv.vchSig1, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(activeMasternodeInfo.legacyKeyIDOperator, mnv.vchSig1, strMessage, strError)) { LogPrintf("MasternodeMan::SendVerifyReply -- VerifyMessage() failed, error: %s\n", strError); return; } @@ -1238,6 +1422,9 @@ void CMasternodeMan::ProcessVerifyReply(CNode* pnode, CMasternodeVerification& m { AssertLockHeld(cs_main); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + std::string strError; // did we even ask for it? if that's the case we should have matching fulfilled request @@ -1280,7 +1467,7 @@ void CMasternodeMan::ProcessVerifyReply(CNode* pnode, CMasternodeVerification& m { LOCK(cs); - CMasternode* prealMasternode = NULL; + CMasternode* prealMasternode = nullptr; std::vector vpMasternodesToBan; uint256 hash1 = mnv.GetSignatureHash1(blockHash); @@ -1290,10 +1477,10 @@ void CMasternodeMan::ProcessVerifyReply(CNode* pnode, CMasternodeVerification& m if(CAddress(mnpair.second.addr, NODE_NETWORK) == pnode->addr) { bool fFound = false; if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { - fFound = CHashSigner::VerifyHash(hash1, mnpair.second.pubKeyMasternode, mnv.vchSig1, strError); + fFound = CHashSigner::VerifyHash(hash1, mnpair.second.legacyKeyIDOperator, mnv.vchSig1, strError); // we don't care about mnv with signature in old format } else { - fFound = CMessageSigner::VerifyMessage(mnpair.second.pubKeyMasternode, mnv.vchSig1, strMessage1, strError); + fFound = CMessageSigner::VerifyMessage(mnpair.second.legacyKeyIDOperator, mnv.vchSig1, strMessage1, strError); } if (fFound) { // found it! @@ -1304,23 +1491,23 @@ void CMasternodeMan::ProcessVerifyReply(CNode* pnode, CMasternodeVerification& m netfulfilledman.AddFulfilledRequest(pnode->addr, strprintf("%s", NetMsgType::MNVERIFY)+"-done"); // we can only broadcast it if we are an activated masternode - if(activeMasternode.outpoint.IsNull()) continue; + if(activeMasternodeInfo.outpoint.IsNull()) continue; // update ... mnv.addr = mnpair.second.addr; mnv.masternodeOutpoint1 = mnpair.second.outpoint; - mnv.masternodeOutpoint2 = activeMasternode.outpoint; + mnv.masternodeOutpoint2 = activeMasternodeInfo.outpoint; // ... and sign it std::string strError; if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash2 = mnv.GetSignatureHash2(blockHash); - if(!CHashSigner::SignHash(hash2, activeMasternode.keyMasternode, mnv.vchSig2)) { + if(!CHashSigner::SignHash(hash2, activeMasternodeInfo.legacyKeyOperator, mnv.vchSig2)) { LogPrintf("MasternodeMan::ProcessVerifyReply -- SignHash() failed\n"); return; } - if(!CHashSigner::VerifyHash(hash2, activeMasternode.pubKeyMasternode, mnv.vchSig2, strError)) { + if(!CHashSigner::VerifyHash(hash2, activeMasternodeInfo.legacyKeyIDOperator, mnv.vchSig2, strError)) { LogPrintf("MasternodeMan::ProcessVerifyReply -- VerifyHash() failed, error: %s\n", strError); return; } @@ -1328,12 +1515,12 @@ void CMasternodeMan::ProcessVerifyReply(CNode* pnode, CMasternodeVerification& m std::string strMessage2 = strprintf("%s%d%s%s%s", mnv.addr.ToString(false), mnv.nonce, blockHash.ToString(), mnv.masternodeOutpoint1.ToStringShort(), mnv.masternodeOutpoint2.ToStringShort()); - if(!CMessageSigner::SignMessage(strMessage2, mnv.vchSig2, activeMasternode.keyMasternode)) { + if(!CMessageSigner::SignMessage(strMessage2, mnv.vchSig2, activeMasternodeInfo.legacyKeyOperator)) { LogPrintf("MasternodeMan::ProcessVerifyReply -- SignMessage() failed\n"); return; } - if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, mnv.vchSig2, strMessage2, strError)) { + if(!CMessageSigner::VerifyMessage(activeMasternodeInfo.legacyKeyIDOperator, mnv.vchSig2, strMessage2, strError)) { LogPrintf("MasternodeMan::ProcessVerifyReply -- VerifyMessage() failed, error: %s\n", strError); return; } @@ -1374,6 +1561,9 @@ void CMasternodeMan::ProcessVerifyBroadcast(CNode* pnode, const CMasternodeVerif { AssertLockHeld(cs_main); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + std::string strError; if(mapSeenMasternodeVerification.find(mnv.GetHash()) != mapSeenMasternodeVerification.end()) { @@ -1443,12 +1633,12 @@ void CMasternodeMan::ProcessVerifyBroadcast(CNode* pnode, const CMasternodeVerif uint256 hash1 = mnv.GetSignatureHash1(blockHash); uint256 hash2 = mnv.GetSignatureHash2(blockHash); - if(!CHashSigner::VerifyHash(hash1, pmn1->pubKeyMasternode, mnv.vchSig1, strError)) { + if(!CHashSigner::VerifyHash(hash1, pmn1->legacyKeyIDOperator, mnv.vchSig1, strError)) { LogPrintf("MasternodeMan::ProcessVerifyBroadcast -- VerifyHash() failed, error: %s\n", strError); return; } - if(!CHashSigner::VerifyHash(hash2, pmn2->pubKeyMasternode, mnv.vchSig2, strError)) { + if(!CHashSigner::VerifyHash(hash2, pmn2->legacyKeyIDOperator, mnv.vchSig2, strError)) { LogPrintf("MasternodeMan::ProcessVerifyBroadcast -- VerifyHash() failed, error: %s\n", strError); return; } @@ -1457,12 +1647,12 @@ void CMasternodeMan::ProcessVerifyBroadcast(CNode* pnode, const CMasternodeVerif std::string strMessage2 = strprintf("%s%d%s%s%s", mnv.addr.ToString(false), mnv.nonce, blockHash.ToString(), mnv.masternodeOutpoint1.ToStringShort(), mnv.masternodeOutpoint2.ToStringShort()); - if(!CMessageSigner::VerifyMessage(pmn1->pubKeyMasternode, mnv.vchSig1, strMessage1, strError)) { + if(!CMessageSigner::VerifyMessage(pmn1->legacyKeyIDOperator, mnv.vchSig1, strMessage1, strError)) { LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- VerifyMessage() for masternode1 failed, error: %s\n", strError); return; } - if(!CMessageSigner::VerifyMessage(pmn2->pubKeyMasternode, mnv.vchSig2, strMessage2, strError)) { + if(!CMessageSigner::VerifyMessage(pmn2->legacyKeyIDOperator, mnv.vchSig2, strMessage2, strError)) { LogPrintf("CMasternodeMan::ProcessVerifyBroadcast -- VerifyMessage() for masternode2 failed, error: %s\n", strError); return; } @@ -1495,12 +1685,17 @@ std::string CMasternodeMan::ToString() const { std::ostringstream info; - info << "Masternodes: " << (int)mapMasternodes.size() << - ", peers who asked us for Masternode list: " << (int)mAskedUsForMasternodeList.size() << - ", peers we asked for Masternode list: " << (int)mWeAskedForMasternodeList.size() << - ", entries in Masternode list we asked for: " << (int)mWeAskedForMasternodeListEntry.size() << - ", nDsqCount: " << (int)nDsqCount; - + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + info << "Masternodes: masternode object count: " << (int)mapMasternodes.size() << + ", deterministic masternode count: " << deterministicMNManager->GetListAtChainTip().GetAllMNsCount() << + ", nDsqCount: " << (int)nDsqCount; + } else { + info << "Masternodes: " << (int)mapMasternodes.size() << + ", peers who asked us for Masternode list: " << (int)mAskedUsForMasternodeList.size() << + ", peers we asked for Masternode list: " << (int)mWeAskedForMasternodeList.size() << + ", entries in Masternode list we asked for: " << (int)mWeAskedForMasternodeListEntry.size() << + ", nDsqCount: " << (int)nDsqCount; + } return info.str(); } @@ -1509,6 +1704,9 @@ bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CNode* pfrom, CMasternodeBr // Need to lock cs_main here to ensure consistent locking order because the SimpleCheck call below locks cs_main LOCK(cs_main); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return false; + { LOCK(cs); nDos = 0; @@ -1574,13 +1772,13 @@ bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CNode* pfrom, CMasternodeBr Add(mnb); masternodeSync.BumpAssetLastTime("CMasternodeMan::CheckMnbAndUpdateMasternodeList - new"); // if it matches our Masternode privkey... - if(fMasternodeMode && mnb.pubKeyMasternode == activeMasternode.pubKeyMasternode) { + if(fMasternodeMode && mnb.legacyKeyIDOperator == activeMasternodeInfo.legacyKeyIDOperator) { mnb.nPoSeBanScore = -MASTERNODE_POSE_BAN_MAX_SCORE; if(mnb.nProtocolVersion == PROTOCOL_VERSION) { // ... and PROTOCOL_VERSION, then we've been remotely activated ... LogPrintf("CMasternodeMan::CheckMnbAndUpdateMasternodeList -- Got NEW Masternode entry: masternode=%s sigTime=%lld addr=%s\n", mnb.outpoint.ToStringShort(), mnb.sigTime, mnb.addr.ToString()); - activeMasternode.ManageState(connman); + legacyActiveMasternodeManager.ManageState(connman); } else { // ... otherwise we need to reactivate our node, do not add it to the list and do not relay // but also do not ban the node we get this message from @@ -1599,7 +1797,7 @@ bool CMasternodeMan::CheckMnbAndUpdateMasternodeList(CNode* pfrom, CMasternodeBr void CMasternodeMan::UpdateLastPaid(const CBlockIndex* pindex) { - LOCK(cs); + LOCK2(cs_main, cs); if(fLiteMode || !masternodeSync.IsWinnersListSynced() || mapMasternodes.empty()) return; @@ -1621,6 +1819,8 @@ void CMasternodeMan::UpdateLastPaid(const CBlockIndex* pindex) void CMasternodeMan::UpdateLastSentinelPingTime() { LOCK(cs); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; nLastSentinelPingTime = GetTime(); } @@ -1650,11 +1850,13 @@ void CMasternodeMan::RemoveGovernanceObject(uint256 nGovernanceObjectHash) } } -void CMasternodeMan::CheckMasternode(const CPubKey& pubKeyMasternode, bool fForce) +void CMasternodeMan::CheckMasternode(const CKeyID& keyIDOperator, bool fForce) { LOCK2(cs_main, cs); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; for (auto& mnpair : mapMasternodes) { - if (mnpair.second.pubKeyMasternode == pubKeyMasternode) { + if (mnpair.second.legacyKeyIDOperator == keyIDOperator) { mnpair.second.Check(fForce); return; } @@ -1671,6 +1873,8 @@ bool CMasternodeMan::IsMasternodePingedWithin(const COutPoint& outpoint, int nSe void CMasternodeMan::SetMasternodeLastPing(const COutPoint& outpoint, const CMasternodePing& mnp) { LOCK(cs); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; CMasternode* pmn = Find(outpoint); if(!pmn) { return; @@ -1693,6 +1897,9 @@ void CMasternodeMan::UpdatedBlockTip(const CBlockIndex *pindex) nCachedBlockHeight = pindex->nHeight; LogPrint("masternode", "CMasternodeMan::UpdatedBlockTip -- nCachedBlockHeight=%d\n", nCachedBlockHeight); + AddDeterministicMasternodes(); + RemoveNonDeterministicMasternodes(); + CheckSameAddr(); if(fMasternodeMode) { @@ -1742,7 +1949,7 @@ void CMasternodeMan::WarnMasternodeDaemonUpdates() fWarned = true; } -void CMasternodeMan::NotifyMasternodeUpdates(CConnman& connman) +void CMasternodeMan::NotifyMasternodeUpdates(CConnman& connman, bool forceAddedChecks, bool forceRemovedChecks) { // Avoid double locking bool fMasternodesAddedLocal = false; @@ -1753,11 +1960,11 @@ void CMasternodeMan::NotifyMasternodeUpdates(CConnman& connman) fMasternodesRemovedLocal = fMasternodesRemoved; } - if(fMasternodesAddedLocal) { + if(fMasternodesAddedLocal || forceAddedChecks) { governance.CheckMasternodeOrphanObjects(connman); governance.CheckMasternodeOrphanVotes(connman); } - if(fMasternodesRemovedLocal) { + if(fMasternodesRemovedLocal || forceRemovedChecks) { governance.UpdateCachesAndClean(); } @@ -1765,3 +1972,31 @@ void CMasternodeMan::NotifyMasternodeUpdates(CConnman& connman) fMasternodesAdded = false; fMasternodesRemoved = false; } + +void CMasternodeMan::DoMaintenance(CConnman& connman) +{ + if(fLiteMode) return; // disable all Absolute specific functionality + + if(!masternodeSync.IsBlockchainSynced() || ShutdownRequested()) + return; + + static unsigned int nTick = 0; + + nTick++; + + // make sure to check all masternodes first + mnodeman.Check(); + + mnodeman.ProcessPendingMnbRequests(connman); + mnodeman.ProcessPendingMnvRequests(connman); + + if(nTick % 60 == 0) { + mnodeman.ProcessMasternodeConnections(connman); + mnodeman.CheckAndRemove(connman); + mnodeman.WarnMasternodeDaemonUpdates(); + } + + if(fMasternodeMode && (nTick % (60 * 5) == 0)) { + mnodeman.DoFullVerificationStep(connman); + } +} diff --git a/src/masternodeman.h b/src/masternodeman.h index f3afb2460402..a955cb0a9ec1 100644 --- a/src/masternodeman.h +++ b/src/masternodeman.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -136,7 +136,6 @@ class CMasternodeMan /// Ask (source) node for mnb void AskForMN(CNode *pnode, const COutPoint& outpoint, CConnman& connman); - void AskForMnb(CNode *pnode, const uint256 &hash); bool PoSeBan(const COutPoint &outpoint); bool AllowMixing(const COutPoint &outpoint); @@ -150,6 +149,9 @@ class CMasternodeMan /// This is dummy overload to be used for dumping/loading mncache.dat void CheckAndRemove() {} + void AddDeterministicMasternodes(); + void RemoveNonDeterministicMasternodes(); + /// Clear Masternode vector void Clear(); @@ -169,8 +171,9 @@ class CMasternodeMan bool Get(const COutPoint& outpoint, CMasternode& masternodeRet); bool Has(const COutPoint& outpoint); + bool GetMasternodeInfo(const uint256& proTxHash, masternode_info_t& mnInfoRet); bool GetMasternodeInfo(const COutPoint& outpoint, masternode_info_t& mnInfoRet); - bool GetMasternodeInfo(const CPubKey& pubKeyMasternode, masternode_info_t& mnInfoRet); + bool GetMasternodeInfo(const CKeyID& keyIDOperator, masternode_info_t& mnInfoRet); bool GetMasternodeInfo(const CScript& payee, masternode_info_t& mnInfoRet); /// Find an entry in the masternode list that is next to be paid @@ -181,7 +184,7 @@ class CMasternodeMan /// Find a random entry masternode_info_t FindRandomNotInVec(const std::vector &vecToExclude, int nProtocolVersion = -1); - std::map GetFullMasternodeMap() { return mapMasternodes; } + std::map GetFullMasternodeMap(); bool GetMasternodeRanks(rank_pair_vec_t& vecMasternodeRanksRet, int nBlockHeight = -1, int nMinProtocol = 0); bool GetMasternodeRank(const COutPoint &outpoint, int& nRankRet, int nBlockHeight = -1, int nMinProtocol = 0); @@ -194,7 +197,8 @@ class CMasternodeMan void DoFullVerificationStep(CConnman& connman); void CheckSameAddr(); - bool SendVerifyRequest(const CAddress& addr, const std::vector& vSortedByAddr, CConnman& connman); + bool CheckVerifyRequestAddr(const CAddress& addr, CConnman& connman); + void PrepareVerifyRequest(const CAddress& addr, CConnman& connman); void ProcessPendingMnvRequests(CConnman& connman); void SendVerifyReply(CNode* pnode, CMasternodeVerification& mnv, CConnman& connman); void ProcessVerifyReply(CNode* pnode, CMasternodeVerification& mnv); @@ -222,7 +226,7 @@ class CMasternodeMan LOCK(cs); std::vector vecTmp = vecDirtyGovernanceObjectHashes; vecDirtyGovernanceObjectHashes.clear(); - return vecTmp;; + return vecTmp; } bool IsSentinelPingActive(); @@ -230,7 +234,7 @@ class CMasternodeMan bool AddGovernanceVote(const COutPoint& outpoint, uint256 nGovernanceObjectHash); void RemoveGovernanceObject(uint256 nGovernanceObjectHash); - void CheckMasternode(const CPubKey& pubKeyMasternode, bool fForce); + void CheckMasternode(const CKeyID& keyIDOperator, bool fForce); bool IsMasternodePingedWithin(const COutPoint& outpoint, int nSeconds, int64_t nTimeToCheckAt = -1); void SetMasternodeLastPing(const COutPoint& outpoint, const CMasternodePing& mnp); @@ -243,8 +247,9 @@ class CMasternodeMan * Called to notify CGovernanceManager that the masternode index has been updated. * Must be called while not holding the CMasternodeMan::cs mutex */ - void NotifyMasternodeUpdates(CConnman& connman); + void NotifyMasternodeUpdates(CConnman& connman, bool forceAddedChecks = false, bool forceRemovedChecks = false); + void DoMaintenance(CConnman &connman); }; #endif diff --git a/src/messagesigner.cpp b/src/messagesigner.cpp index cb750b344ce0..a123f16732ee 100644 --- a/src/messagesigner.cpp +++ b/src/messagesigner.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -32,12 +32,17 @@ bool CMessageSigner::SignMessage(const std::string& strMessage, std::vector& vchSig, const std::string& strMessage, std::string& strErrorRet) +{ + return VerifyMessage(pubkey.GetID(), vchSig, strMessage, strErrorRet); +} + +bool CMessageSigner::VerifyMessage(const CKeyID& keyID, const std::vector& vchSig, const std::string& strMessage, std::string& strErrorRet) { CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; - return CHashSigner::VerifyHash(ss.GetHash(), pubkey, vchSig, strErrorRet); + return CHashSigner::VerifyHash(ss.GetHash(), keyID, vchSig, strErrorRet); } bool CHashSigner::SignHash(const uint256& hash, const CKey& key, std::vector& vchSigRet) @@ -46,6 +51,11 @@ bool CHashSigner::SignHash(const uint256& hash, const CKey& key, std::vector& vchSig, std::string& strErrorRet) +{ + return VerifyHash(hash, pubkey.GetID(), vchSig, strErrorRet); +} + +bool CHashSigner::VerifyHash(const uint256& hash, const CKeyID& keyID, const std::vector& vchSig, std::string& strErrorRet) { CPubKey pubkeyFromSig; if(!pubkeyFromSig.RecoverCompact(hash, vchSig)) { @@ -53,9 +63,9 @@ bool CHashSigner::VerifyHash(const uint256& hash, const CPubKey& pubkey, const s return false; } - if(pubkeyFromSig.GetID() != pubkey.GetID()) { + if(pubkeyFromSig.GetID() != keyID) { strErrorRet = strprintf("Keys don't match: pubkey=%s, pubkeyFromSig=%s, hash=%s, vchSig=%s", - pubkey.GetID().ToString(), pubkeyFromSig.GetID().ToString(), hash.ToString(), + keyID.ToString(), pubkeyFromSig.GetID().ToString(), hash.ToString(), EncodeBase64(&vchSig[0], vchSig.size())); return false; } diff --git a/src/messagesigner.h b/src/messagesigner.h index bf4a9fe9d989..0a35c4003496 100644 --- a/src/messagesigner.h +++ b/src/messagesigner.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -19,6 +19,8 @@ class CMessageSigner static bool SignMessage(const std::string& strMessage, std::vector& vchSigRet, const CKey& key); /// Verify the message signature, returns true if succcessful static bool VerifyMessage(const CPubKey& pubkey, const std::vector& vchSig, const std::string& strMessage, std::string& strErrorRet); + /// Verify the message signature, returns true if succcessful + static bool VerifyMessage(const CKeyID& keyID, const std::vector& vchSig, const std::string& strMessage, std::string& strErrorRet); }; /** Helper class for signing hashes and checking their signatures @@ -30,6 +32,8 @@ class CHashSigner static bool SignHash(const uint256& hash, const CKey& key, std::vector& vchSigRet); /// Verify the hash signature, returns true if succcessful static bool VerifyHash(const uint256& hash, const CPubKey& pubkey, const std::vector& vchSig, std::string& strErrorRet); + /// Verify the hash signature, returns true if succcessful + static bool VerifyHash(const uint256& hash, const CKeyID& keyID, const std::vector& vchSig, std::string& strErrorRet); }; #endif diff --git a/src/miner.cpp b/src/miner.cpp index 2272e1a94d48..aaaf3840d1fa 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -29,6 +29,11 @@ #include "masternode-sync.h" #include "validationinterface.h" +#include "evo/specialtx.h" +#include "evo/cbtx.h" +#include "evo/simplifiedmns.h" +#include "evo/deterministicmns.h" + #include #include #include @@ -128,6 +133,9 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOps.push_back(-1); // updated at end LOCK2(cs_main, mempool.cs); + + bool fAIP0003Active_context = VersionBitsState(chainActive.Tip(), chainparams.GetConsensus(), Consensus::DEPLOYMENT_AIP0003, versionbitscache) == THRESHOLD_ACTIVE; + CBlockIndex* pindexPrev = chainActive.Tip(); nHeight = pindexPrev->nHeight + 1; @@ -168,13 +176,31 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc // Compute regular coinbase transaction. coinbaseTx.vout[0].nValue = blockReward; - coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + + if (!fAIP0003Active_context) { + coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + } else { + coinbaseTx.vin[0].scriptSig = CScript() << OP_RETURN; + + coinbaseTx.nVersion = 3; + coinbaseTx.nType = TRANSACTION_COINBASE; + + CCbTx cbTx; + cbTx.nHeight = nHeight; + + CValidationState state; + if (!CalcCbTxMerkleRootMNList(*pblock, pindexPrev, cbTx.merkleRootMNList, state)) { + throw std::runtime_error(strprintf("%s: CalcSMLMerkleRootForNewBlock failed: %s", __func__, FormatStateMessage(state))); + } + + SetTxPayload(coinbaseTx, cbTx); + } // Update coinbase transaction with additional info about masternode and governance payments, // get some info back to pass to getblocktemplate - FillBlockPayments(coinbaseTx, nHeight, blockReward, pblock->txoutMasternode, pblock->voutSuperblock); + FillBlockPayments(coinbaseTx, nHeight, blockReward, pblocktemplate->voutMasternodePayments, pblocktemplate->voutSuperblockPayments); // LogPrintf("CreateNewBlock -- nBlockHeight %d blockReward %lld txoutMasternode %s coinbaseTx %s", - // nHeight, blockReward, pblock->txoutMasternode.ToString(), coinbaseTx.ToString()); + // nHeight, blockReward, pblocktemplate->txoutsMasternode.ToString(), coinbaseTx.ToString()); pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vTxFees[0] = -nFees; @@ -184,6 +210,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); pblock->nNonce = 0; + pblocktemplate->nPrevBits = pindexPrev->nBits; pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(*pblock->vtx[0]); CValidationState state; diff --git a/src/miner.h b/src/miner.h index de46fe2c7c9b..c73bc626ca63 100644 --- a/src/miner.h +++ b/src/miner.h @@ -30,6 +30,9 @@ struct CBlockTemplate CBlock block; std::vector vTxFees; std::vector vTxSigOps; + uint32_t nPrevBits; // nBits of previous block (for subsidy calculation) + std::vector voutMasternodePayments; // masternode payment + std::vector voutSuperblockPayments; // superblock payment }; // Container for tracking updates to ancestor feerate as we include (parent) diff --git a/src/net.cpp b/src/net.cpp index 1d67ac5a7bf3..a83dbe775767 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -206,7 +206,7 @@ void AdvertiseLocal(CNode *pnode) // learn a new local address bool AddLocal(const CService& addr, int nScore) { - if (!addr.IsRoutable()) + if (!addr.IsRoutable() && Params().RequireRoutableExternalIP()) return false; if (!fDiscover && nScore < LOCAL_MANUAL) @@ -2239,8 +2239,8 @@ void CConnman::SetNetworkActive(bool active) } CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In) : - nSeed0(nSeed0In), nSeed1(nSeed1In), - addrman(Params().AllowMultiplePorts()) + addrman(Params().AllowMultiplePorts()), + nSeed0(nSeed0In), nSeed1(nSeed1In) { fNetworkActive = true; setBannedIsDirty = false; @@ -2627,6 +2627,21 @@ void CConnman::RelayInv(CInv &inv, const int minProtoVersion) { pnode->PushInventory(inv); } +void CConnman::RelayInvFiltered(CInv &inv, const CTransaction& relatedTx, const int minProtoVersion) +{ + LOCK(cs_vNodes); + for (const auto& pnode : vNodes) { + if(pnode->nVersion < minProtoVersion) + continue; + { + LOCK(pnode->cs_filter); + if(pnode->pfilter && !pnode->pfilter->IsRelevantAndUpdate(relatedTx)) + continue; + } + pnode->PushInventory(inv); + } +} + void CConnman::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); diff --git a/src/net.h b/src/net.h index cd135e5f230d..03ef7069c181 100644 --- a/src/net.h +++ b/src/net.h @@ -65,7 +65,7 @@ static const int MAX_OUTBOUND_CONNECTIONS = 8; /** Maximum number of addnode outgoing nodes */ static const int MAX_ADDNODE_CONNECTIONS = 8; /** Maximum number if outgoing masternodes */ -static const int MAX_OUTBOUND_MASTERNODE_CONNECTIONS = 20; +static const int MAX_OUTBOUND_MASTERNODE_CONNECTIONS = 30; /** -listen default */ static const bool DEFAULT_LISTEN = true; /** -upnp default */ @@ -309,6 +309,7 @@ class CConnman void RelayTransaction(const CTransaction& tx); void RelayTransaction(const CTransaction& tx, const CDataStream& ss); void RelayInv(CInv &inv, const int minProtoVersion = MIN_PEER_PROTO_VERSION); + void RelayInvFiltered(CInv &inv, const CTransaction &relatedTx, const int minProtoVersion = MIN_PEER_PROTO_VERSION); // Addrman functions size_t GetAddressCount() const; @@ -886,10 +887,15 @@ class CNode void AddInventoryKnown(const CInv& inv) + { + AddInventoryKnown(inv.hash); + } + + void AddInventoryKnown(const uint256& hash) { { LOCK(cs_inventory); - filterInventoryKnown.insert(inv.hash); + filterInventoryKnown.insert(hash); } } @@ -907,8 +913,12 @@ class CNode LogPrint("net", "PushInventory -- inv: %s peer=%d\n", inv.ToString(), id); vInventoryBlockToSend.push_back(inv.hash); } else { - LogPrint("net", "PushInventory -- inv: %s peer=%d\n", inv.ToString(), id); - vInventoryOtherToSend.push_back(inv); + if (!filterInventoryKnown.contains(inv.hash)) { + LogPrint("net", "PushInventory -- inv: %s peer=%d\n", inv.ToString(), id); + vInventoryOtherToSend.push_back(inv); + } else { + LogPrint("net", "PushInventory -- filtered inv: %s peer=%d\n", inv.ToString(), id); + } } } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 670940f2ea19..9aa895883edf 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -42,6 +42,9 @@ #endif // ENABLE_WALLET #include "privatesend-server.h" +#include "evo/deterministicmns.h" +#include "evo/simplifiedmns.h" + #include #if defined(NDEBUG) @@ -911,12 +914,12 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) case MSG_BLOCK: return mapBlockIndex.count(inv.hash); - /* + /* Absolute Related Inventory Messages -- - We shouldn't update the sync times for each of the messages when we already have it. + We shouldn't update the sync times for each of the messages when we already have it. We're going to be asking many nodes upfront for the full inventory list, so we'll get duplicates of these. We want to only update the time on new hits, so that we can time out appropriately if needed. */ @@ -927,7 +930,10 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) return instantsend.AlreadyHave(inv.hash); case MSG_SPORK: - return mapSporks.count(inv.hash); + { + CSporkMessage spork; + return sporkManager.GetSporkByHash(inv.hash, spork); + } case MSG_MASTERNODE_PAYMENT_VOTE: return mnpayments.mapMasternodePaymentVotes.count(inv.hash); @@ -1164,46 +1170,55 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } if (!push && inv.type == MSG_SPORK) { - if(mapSporks.count(inv.hash)) { - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SPORK, mapSporks[inv.hash])); + CSporkMessage spork; + if(sporkManager.GetSporkByHash(inv.hash, spork)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SPORK, spork)); push = true; } } if (!push && inv.type == MSG_MASTERNODE_PAYMENT_VOTE) { - if(mnpayments.HasVerifiedPaymentVote(inv.hash)) { - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTVOTE, mnpayments.mapMasternodePaymentVotes[inv.hash])); - push = true; + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) { + if (mnpayments.HasVerifiedPaymentVote(inv.hash)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTVOTE, mnpayments.mapMasternodePaymentVotes[inv.hash])); + push = true; + } } } if (!push && inv.type == MSG_MASTERNODE_PAYMENT_BLOCK) { - BlockMap::iterator mi = mapBlockIndex.find(inv.hash); - LOCK(cs_mapMasternodeBlocks); - if (mi != mapBlockIndex.end() && mnpayments.mapMasternodeBlocks.count(mi->second->nHeight)) { - BOOST_FOREACH(CMasternodePayee& payee, mnpayments.mapMasternodeBlocks[mi->second->nHeight].vecPayees) { - std::vector vecVoteHashes = payee.GetVoteHashes(); - BOOST_FOREACH(uint256& hash, vecVoteHashes) { - if(mnpayments.HasVerifiedPaymentVote(hash)) { - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTVOTE, mnpayments.mapMasternodePaymentVotes[hash])); + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) { + BlockMap::iterator mi = mapBlockIndex.find(inv.hash); + LOCK(cs_mapMasternodeBlocks); + if (mi != mapBlockIndex.end() && mnpayments.mapMasternodeBlocks.count(mi->second->nHeight)) { + BOOST_FOREACH(CMasternodePayee& payee, mnpayments.mapMasternodeBlocks[mi->second->nHeight].vecPayees) { + std::vector vecVoteHashes = payee.GetVoteHashes(); + BOOST_FOREACH(uint256& hash, vecVoteHashes) { + if(mnpayments.HasVerifiedPaymentVote(hash)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MASTERNODEPAYMENTVOTE, mnpayments.mapMasternodePaymentVotes[hash])); + } } } + push = true; } - push = true; } } if (!push && inv.type == MSG_MASTERNODE_ANNOUNCE) { - if(mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)){ - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNANNOUNCE, mnodeman.mapSeenMasternodeBroadcast[inv.hash].second)); - push = true; + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) { + if (mnodeman.mapSeenMasternodeBroadcast.count(inv.hash)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNANNOUNCE, mnodeman.mapSeenMasternodeBroadcast[inv.hash].second)); + push = true; + } } } if (!push && inv.type == MSG_MASTERNODE_PING) { - if(mnodeman.mapSeenMasternodePing.count(inv.hash)) { - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNPING, mnodeman.mapSeenMasternodePing[inv.hash])); - push = true; + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) { + if (mnodeman.mapSeenMasternodePing.count(inv.hash)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNPING, mnodeman.mapSeenMasternodePing[inv.hash])); + push = true; + } } } @@ -1431,6 +1446,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (Params().NetworkIDString() == CBaseChainParams::POVNET) { if (strSubVer.find(strprintf("povnet=%s", GetPoVNETName())) == std::string::npos) { + LOCK(cs_main); LogPrintf("connected to wrong PoVNET. Reported version is %s, expected povnet name is %s\n", strSubVer, GetPoVNETName()); if (!pfrom->fInbound) Misbehaving(pfrom->GetId(), 100); // don't try to connect again @@ -1927,10 +1943,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr CTxLockRequest txLockRequest; CDarksendBroadcastTx dstx; int nInvType = MSG_TX; + bool fCanAutoLock = false; // Read data and assign inv type if(strCommand == NetMsgType::TX) { vRecv >> ptx; + txLockRequest = CTxLockRequest(ptx); + fCanAutoLock = CInstantSend::CanAutoLock() && txLockRequest.IsSimple(); } else if(strCommand == NetMsgType::TXLOCKREQUEST) { vRecv >> txLockRequest; ptx = txLockRequest.tx; @@ -1947,10 +1966,16 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->setAskFor.erase(inv.hash); // Process custom logic, no matter if tx will be accepted to mempool later or not - if (strCommand == NetMsgType::TXLOCKREQUEST) { + if (strCommand == NetMsgType::TXLOCKREQUEST || fCanAutoLock) { if(!instantsend.ProcessTxLockRequest(txLockRequest, connman)) { LogPrint("instantsend", "TXLOCKREQUEST -- failed %s\n", txLockRequest.GetHash().ToString()); - return false; + // Should not really happen for "fCanAutoLock == true" but just in case: + if (!fCanAutoLock) { + // Fail only for "true" IS here + return false; + } + // Fallback for normal txes to process as usual + fCanAutoLock = false; } } else if (strCommand == NetMsgType::DSTX) { uint256 hashTx = tx.GetHash(); @@ -1973,7 +1998,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // we have no idea about (e.g we were offline)? How to handle them? } - if(!dstx.CheckSignature(mn.pubKeyMasternode)) { + if (!dstx.CheckSignature(mn.legacyKeyIDOperator, mn.blsPubKeyOperator)) { LogPrint("privatesend", "DSTX -- CheckSignature() failed for %s\n", hashTx.ToString()); return false; } @@ -1990,15 +2015,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr mapAlreadyAskedFor.erase(inv.hash); - std::list lRemovedTxn; - - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs, &lRemovedTxn)) { + if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs)) { // Process custom txes, this changes AlreadyHave to "true" if (strCommand == NetMsgType::DSTX) { LogPrintf("DSTX -- Masternode transaction accepted, txid=%s, peer=%d\n", tx.GetHash().ToString(), pfrom->id); CPrivateSend::AddDSTX(dstx); - } else if (strCommand == NetMsgType::TXLOCKREQUEST) { + } else if (strCommand == NetMsgType::TXLOCKREQUEST || fCanAutoLock) { LogPrintf("TXLOCKREQUEST -- Transaction Lock Request accepted, txid=%s, peer=%d\n", tx.GetHash().ToString(), pfrom->id); instantsend.AcceptLockRequest(txLockRequest); @@ -2042,7 +2065,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, true, &fMissingInputs2, &lRemovedTxn)) { + if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); connman.RelayTransaction(orphanTx); for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { @@ -2145,9 +2168,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } } - for (const CTransactionRef& removedTx : lRemovedTxn) - AddToCompactExtraTransactions(removedTx); - int nDoS = 0; if (state.IsInvalid(nDoS)) { @@ -2812,6 +2832,31 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } + else if (strCommand == NetMsgType::GETMNLISTDIFF) { + CGetSimplifiedMNListDiff cmd; + vRecv >> cmd; + + LOCK(cs_main); + + CSimplifiedMNListDiff mnListDiff; + std::string strError; + if (BuildSimplifiedMNListDiff(cmd.baseBlockHash, cmd.blockHash, mnListDiff, strError)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MNLISTDIFF, mnListDiff)); + } else { + LogPrint("net", "getmnlistdiff failed for baseBlockHash=%s, blockHash=%s. error=%s\n", cmd.baseBlockHash.ToString(), cmd.blockHash.ToString(), strError); + Misbehaving(pfrom->id, 1); + } + } + + + else if (strCommand == NetMsgType::MNLISTDIFF) { + // we have never requested this + LOCK(cs_main); + Misbehaving(pfrom->id, 100); + LogPrint("net", "received not-requested mnlistdiff. peer=%d\n", pfrom->id); + } + + else if (strCommand == NetMsgType::NOTFOUND) { // We do not care about the NOTFOUND message, but logging an Unknown Command // message would be undesirable as we transmit it ourselves. @@ -3276,7 +3321,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr std::vector vInv; { LOCK(pto->cs_inventory); - vInv.reserve(std::max(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX)); + vInv.reserve(std::max(pto->vInventoryBlockToSend.size(), INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK * MaxBlockSize(true) / 1000000)); // Add blocks BOOST_FOREACH(const uint256& hash, pto->vInventoryBlockToSend) { @@ -3345,7 +3390,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr // especially since we have many peers and some will draw much shorter delays. unsigned int nRelayedTransactions = 0; LOCK(pto->cs_filter); - while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX) { + while (!vInvTx.empty() && nRelayedTransactions < INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK * MaxBlockSize(true) / 1000000) { // Fetch the top element from the heap std::pop_heap(vInvTx.begin(), vInvTx.end(), compareInvMempoolOrder); std::set::iterator it = vInvTx.back(); @@ -3389,7 +3434,11 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr // Send non-tx/non-block inventory items for (const auto& inv : pto->vInventoryOtherToSend) { + if (pto->filterInventoryKnown.contains(inv.hash)) { + continue; + } vInv.push_back(inv); + pto->filterInventoryKnown.insert(inv.hash); if (vInv.size() == MAX_INV_SZ) { connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); diff --git a/src/netfulfilledman.cpp b/src/netfulfilledman.cpp index aeaee598fa5c..baf63c72355b 100644 --- a/src/netfulfilledman.cpp +++ b/src/netfulfilledman.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/netfulfilledman.h b/src/netfulfilledman.h index e70d01787fb6..a8a2fda6a15d 100644 --- a/src/netfulfilledman.h +++ b/src/netfulfilledman.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -45,6 +45,8 @@ class CNetFulfilledRequestManager void Clear(); std::string ToString() const; + + void DoMaintenance() { CheckAndRemove(); } }; #endif diff --git a/src/noui.cpp b/src/noui.cpp index 082750ded990..3428c4102d3d 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/policy/rbf.cpp b/src/policy/rbf.cpp deleted file mode 100644 index d9b47e71bbe0..000000000000 --- a/src/policy/rbf.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2016 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "policy/rbf.h" - -bool SignalsOptInRBF(const CTransaction &tx) -{ - BOOST_FOREACH(const CTxIn &txin, tx.vin) { - if (txin.nSequence < std::numeric_limits::max()-1) { - return true; - } - } - return false; -} - -RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool) -{ - AssertLockHeld(pool.cs); - - CTxMemPool::setEntries setAncestors; - - // First check the transaction itself. - if (SignalsOptInRBF(tx)) { - return RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125; - } - - // If this transaction is not in our mempool, then we can't be sure - // we will know about all its inputs. - if (!pool.exists(tx.GetHash())) { - return RBF_TRANSACTIONSTATE_UNKNOWN; - } - - // If all the inputs have nSequence >= maxint-1, it still might be - // signaled for RBF if any unconfirmed parents have signaled. - uint64_t noLimit = std::numeric_limits::max(); - std::string dummy; - CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash()); - pool.CalculateMemPoolAncestors(entry, setAncestors, noLimit, noLimit, noLimit, noLimit, dummy, false); - - BOOST_FOREACH(CTxMemPool::txiter it, setAncestors) { - if (SignalsOptInRBF(it->GetTx())) { - return RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125; - } - } - return RBF_TRANSACTIONSTATE_FINAL; -} diff --git a/src/policy/rbf.h b/src/policy/rbf.h deleted file mode 100644 index 139aec57602c..000000000000 --- a/src/policy/rbf.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2016 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_POLICY_RBF_H -#define BITCOIN_POLICY_RBF_H - -#include "txmempool.h" - -enum RBFTransactionState { - RBF_TRANSACTIONSTATE_UNKNOWN, - RBF_TRANSACTIONSTATE_REPLACEABLE_BIP125, - RBF_TRANSACTIONSTATE_FINAL -}; - -// Check whether the sequence numbers on this transaction are signaling -// opt-in to replace-by-fee, according to BIP 125 -bool SignalsOptInRBF(const CTransaction &tx); - -// Determine whether an in-mempool transaction is signaling opt-in to RBF -// according to BIP 125 -// This involves checking sequence numbers of the transaction, as well -// as the sequence numbers of all in-mempool ancestors. -RBFTransactionState IsRBFOptIn(const CTransaction &tx, CTxMemPool &pool); - -#endif // BITCOIN_POLICY_RBF_H diff --git a/src/primitives/block.h b/src/primitives/block.h index adfccf10ebf1..772cbab5c1ea 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -79,8 +79,6 @@ class CBlock : public CBlockHeader std::vector vtx; // memory only - mutable CTxOut txoutMasternode; // masternode payment - mutable std::vector voutSuperblock; // superblock payment mutable bool fChecked; CBlock() @@ -106,8 +104,6 @@ class CBlock : public CBlockHeader { CBlockHeader::SetNull(); vtx.clear(); - txoutMasternode = CTxOut(); - voutSuperblock.clear(); fChecked = false; } diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index f2afae047c8e..fd8e56ff5dea 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -48,11 +48,11 @@ std::string CTxIn::ToString() const return str; } -CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn) +CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, int nRoundsIn) { nValue = nValueIn; scriptPubKey = scriptPubKeyIn; - nRounds = -10; + nRounds = nRoundsIn; } std::string CTxOut::ToString() const @@ -60,8 +60,8 @@ std::string CTxOut::ToString() const return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30)); } -CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {} +CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nType(TRANSACTION_NORMAL), nLockTime(0) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), nType(tx.nType), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vExtraPayload(tx.vExtraPayload) {} uint256 CMutableTransaction::GetHash() const { @@ -71,12 +71,14 @@ uint256 CMutableTransaction::GetHash() const std::string CMutableTransaction::ToString() const { std::string str; - str += strprintf("CMutableTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", + str += strprintf("CMutableTransaction(hash=%s, ver=%d, type=%d, vin.size=%u, vout.size=%u, nLockTime=%u, vExtraPayload.size=%d)\n", GetHash().ToString().substr(0,10), nVersion, + nType, vin.size(), vout.size(), - nLockTime); + nLockTime, + vExtraPayload.size()); for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) @@ -90,9 +92,9 @@ uint256 CTransaction::ComputeHash() const } /* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ -CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {} -CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), hash(ComputeHash()) {} -CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), nType(TRANSACTION_NORMAL), vin(), vout(), nLockTime(0), hash() {} +CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), nType(tx.nType), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vExtraPayload(tx.vExtraPayload), hash(ComputeHash()) {} +CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), nType(tx.nType), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), vExtraPayload(tx.vExtraPayload), hash(ComputeHash()) {} CAmount CTransaction::GetValueOut() const { @@ -140,12 +142,14 @@ unsigned int CTransaction::GetTotalSize() const std::string CTransaction::ToString() const { std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", + str += strprintf("CTransaction(hash=%s, ver=%d, type=%d, vin.size=%u, vout.size=%u, nLockTime=%u, vExtraPayload.size=%d)\n", GetHash().ToString().substr(0,10), nVersion, + nType, vin.size(), vout.size(), - nLockTime); + nLockTime, + vExtraPayload.size()); for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index b14e5b261d74..e058136b8d30 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -11,6 +11,16 @@ #include "serialize.h" #include "uint256.h" +/** Transaction types */ +enum { + TRANSACTION_NORMAL = 0, + TRANSACTION_PROVIDER_REGISTER = 1, + TRANSACTION_PROVIDER_UPDATE_SERVICE = 2, + TRANSACTION_PROVIDER_UPDATE_REGISTRAR = 3, + TRANSACTION_PROVIDER_UPDATE_REVOKE = 4, + TRANSACTION_COINBASE = 5, +}; + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -142,7 +152,7 @@ class CTxOut SetNull(); } - CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn); + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, int nRoundsIn = -10); ADD_SERIALIZE_METHODS; @@ -169,10 +179,10 @@ class CTxOut // "Dust" is defined in terms of CTransaction::minRelayTxFee, which has units nABS-per-kilobyte. // If you'd pay more than 1/3 in fees to spend something, then we consider it dust. // A typical spendable txout is 34 bytes big, and will need a CTxIn of at least 148 bytes to spend - // i.e. total is 148 + 32 = 182 bytes. Default -minrelaytxfee is 10000 nABS per kB - // and that means that fee per spendable txout is 182 * 10000 / 1000 = 1820 nABS. + // i.e. total is 148 + 34 = 182 bytes. Default -minrelaytxfee is 1000 nABS per kB + // and that means that fee per spendable txout is 182 * 1000 / 1000 = 182 nABS. // So dust is a spendable txout less than 546 * minRelayTxFee / 1000 (in nABS) - // i.e. 1820 * 3 = 5460 nABS with default -minrelaytxfee = minRelayTxFee = 10000 nABS per kB. + // i.e. 182 * 3 = 546 nABS with default -minrelaytxfee = minRelayTxFee = 1000 nABS per kB. if (scriptPubKey.IsUnspendable()) return 0; @@ -209,23 +219,25 @@ class CTransaction { public: // Default transaction version. - static const int32_t CURRENT_VERSION=1; + static const int32_t CURRENT_VERSION=2; // Changing the default transaction version requires a two step process: first // adapting relay policy by bumping MAX_STANDARD_VERSION, and then later date // bumping the default CURRENT_VERSION at which point both CURRENT_VERSION and // MAX_STANDARD_VERSION will be equal. - static const int32_t MAX_STANDARD_VERSION=2; + static const int32_t MAX_STANDARD_VERSION=3; // The local variables are made const to prevent unintended modification // without updating the cached hash value. However, CTransaction is not // actually immutable; deserialization and assignment are implemented, // and bypass the constness. This is safe, as they update the entire // structure, including the hash. - const int32_t nVersion; + const int16_t nVersion; + const int16_t nType; const std::vector vin; const std::vector vout; const uint32_t nLockTime; + const std::vector vExtraPayload; // only available for special transaction types private: /** Memory only. */ @@ -243,10 +255,13 @@ class CTransaction template inline void Serialize(Stream& s) const { - s << this->nVersion; + int32_t n32bitVersion = this->nVersion | (this->nType << 16); + s << n32bitVersion; s << vin; s << vout; s << nLockTime; + if (this->nVersion >= 3 && this->nType != TRANSACTION_NORMAL) + s << vExtraPayload; } /** This deserializing constructor is provided instead of an Unserialize method. @@ -301,10 +316,12 @@ class CTransaction /** A mutable version of CTransaction. */ struct CMutableTransaction { - int32_t nVersion; + int16_t nVersion; + int16_t nType; std::vector vin; std::vector vout; uint32_t nLockTime; + std::vector vExtraPayload; // only available for special transaction types CMutableTransaction(); CMutableTransaction(const CTransaction& tx); @@ -313,10 +330,18 @@ struct CMutableTransaction template inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(this->nVersion); + int32_t n32bitVersion = this->nVersion | (this->nType << 16); + READWRITE(n32bitVersion); + if (ser_action.ForRead()) { + this->nVersion = (int16_t) (n32bitVersion & 0xffff); + this->nType = (int16_t) ((n32bitVersion >> 16) & 0xffff); + } READWRITE(vin); READWRITE(vout); READWRITE(nLockTime); + if (this->nVersion >= 3 && this->nType != TRANSACTION_NORMAL) { + READWRITE(vExtraPayload); + } } template diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index 4cb33684e473..f160943ca730 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "privatesend-client.h" @@ -19,18 +19,22 @@ #include -CPrivateSendClient privateSendClient; +CPrivateSendClientManager privateSendClient; -void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) +void CPrivateSendClientManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { if(fMasternodeMode) return; if(fLiteMode) return; // ignore all absolute related functionality if(!masternodeSync.IsBlockchainSynced()) return; - if(strCommand == NetMsgType::DSQUEUE) { - TRY_LOCK(cs_darksend, lockRecv); - if(!lockRecv) return; + if(!CheckDiskSpace()) { + ResetPool(); + fEnablePrivateSend = false; + LogPrintf("CPrivateSendClientManager::ProcessMessage -- Not enough disk space, disabling PrivateSend.\n"); + return; + } + if(strCommand == NetMsgType::DSQUEUE) { if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrint("privatesend", "DSQUEUE -- peer=%d using obsolete version %i\n", pfrom->id, pfrom->nVersion); connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, @@ -41,23 +45,27 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strComm CDarksendQueue dsq; vRecv >> dsq; - // process every dsq only once - for (const auto& q : vecDarksendQueue) { - if(q == dsq) { - // LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString()); - return; + { + TRY_LOCK(cs_vecqueue, lockRecv); + if(!lockRecv) return; + + // process every dsq only once + for (const auto& q : vecDarksendQueue) { + if(q == dsq) { + // LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString()); + return; + } } - } + } // cs_vecqueue LogPrint("privatesend", "DSQUEUE -- %s new\n", dsq.ToString()); if(dsq.IsExpired()) return; - if(dsq.nInputCount < 0 || dsq.nInputCount > PRIVATESEND_ENTRY_MAX_SIZE) return; masternode_info_t infoMn; if(!mnodeman.GetMasternodeInfo(dsq.masternodeOutpoint, infoMn)) return; - if(!dsq.CheckSignature(infoMn.pubKeyMasternode)) { + if(!dsq.CheckSignature(infoMn.legacyKeyIDOperator, infoMn.blsPubKeyOperator)) { // we probably have outdated info mnodeman.AskForMN(pfrom, dsq.masternodeOutpoint, connman); return; @@ -65,17 +73,20 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strComm // if the queue is ready, submit if we can if(dsq.fReady) { - if(!infoMixingMasternode.fInfoValid) return; - if(infoMixingMasternode.addr != infoMn.addr) { - LogPrintf("DSQUEUE -- message doesn't match current Masternode: infoMixingMasternode=%s, addr=%s\n", infoMixingMasternode.addr.ToString(), infoMn.addr.ToString()); - return; - } - - if(nState == POOL_STATE_QUEUE) { - LogPrint("privatesend", "DSQUEUE -- PrivateSend queue (%s) is ready on masternode %s\n", dsq.ToString(), infoMn.addr.ToString()); - SubmitDenominate(connman); + LOCK(cs_deqsessions); + for (auto& session : deqSessions) { + masternode_info_t mnMixing; + if (session.GetMixingMasternodeInfo(mnMixing) && mnMixing.addr == infoMn.addr && session.GetState() == POOL_STATE_QUEUE) { + LogPrint("privatesend", "DSQUEUE -- PrivateSend queue (%s) is ready on masternode %s\n", dsq.ToString(), infoMn.addr.ToString()); + session.SubmitDenominate(connman); + return; + } } } else { + LOCK(cs_deqsessions); // have to lock this first to avoid deadlocks with cs_vecqueue + TRY_LOCK(cs_vecqueue, lockRecv); + if(!lockRecv) return; + for (const auto& q : vecDarksendQueue) { if(q.masternodeOutpoint == dsq.masternodeOutpoint) { // no way same mn can send another "not yet ready" dsq this soon @@ -95,14 +106,35 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strComm if(!mnodeman.AllowMixing(dsq.masternodeOutpoint)) return; LogPrint("privatesend", "DSQUEUE -- new PrivateSend queue (%s) from masternode %s\n", dsq.ToString(), infoMn.addr.ToString()); - if(infoMixingMasternode.fInfoValid && infoMixingMasternode.outpoint == dsq.masternodeOutpoint) { - dsq.fTried = true; + for (auto& session : deqSessions) { + masternode_info_t mnMixing; + if (session.GetMixingMasternodeInfo(mnMixing) && mnMixing.outpoint == dsq.masternodeOutpoint) { + dsq.fTried = true; + } } vecDarksendQueue.push_back(dsq); dsq.Relay(connman); } - } else if(strCommand == NetMsgType::DSSTATUSUPDATE) { + } else if ( + strCommand == NetMsgType::DSSTATUSUPDATE || + strCommand == NetMsgType::DSFINALTX || + strCommand == NetMsgType::DSCOMPLETE + ) { + LOCK(cs_deqsessions); + for (auto& session : deqSessions) { + session.ProcessMessage(pfrom, strCommand, vRecv, connman); + } + } +} + +void CPrivateSendClientSession::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) +{ + if(fMasternodeMode) return; + if(fLiteMode) return; // ignore all Absolute related functionality + if(!masternodeSync.IsBlockchainSynced()) return; + + if(strCommand == NetMsgType::DSSTATUSUPDATE) { if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrint("privatesend", "DSSTATUSUPDATE -- peer=%d using obsolete version %i\n", pfrom->id, pfrom->nVersion); @@ -210,17 +242,26 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strComm } } -void CPrivateSendClient::ResetPool() +void CPrivateSendClientSession::ResetPool() { - nCachedLastSuccessBlock = 0; txMyCollateral = CMutableTransaction(); - vecMasternodesUsed.clear(); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); } -void CPrivateSendClient::SetNull() +void CPrivateSendClientManager::ResetPool() +{ + LOCK(cs_deqsessions); + nCachedLastSuccessBlock = 0; + vecMasternodesUsed.clear(); + for (auto& session : deqSessions) { + session.ResetPool(); + } + deqSessions.clear(); +} + +void CPrivateSendClientSession::SetNull() { // Client side nEntriesCount = 0; @@ -228,14 +269,16 @@ void CPrivateSendClient::SetNull() infoMixingMasternode = masternode_info_t(); pendingDsaRequest = CPendingDsaRequest(); - CPrivateSendBase::SetNull(); + CPrivateSendBaseSession::SetNull(); } // // Unlock coins after mixing fails or succeeds // -void CPrivateSendClient::UnlockCoins() +void CPrivateSendClientSession::UnlockCoins() { + if (!pwalletMain) return; + while(true) { TRY_LOCK(pwalletMain->cs_wallet, lockWallet); if(!lockWallet) {MilliSleep(50); continue;} @@ -247,13 +290,13 @@ void CPrivateSendClient::UnlockCoins() vecOutPointLocked.clear(); } -std::string CPrivateSendClient::GetStatus() +std::string CPrivateSendClientSession::GetStatus(bool fWaitForBlock) { static int nStatusMessageProgress = 0; nStatusMessageProgress += 10; std::string strSuffix = ""; - if(WaitForAnotherBlock() || !masternodeSync.IsBlockchainSynced()) + if(fWaitForBlock || !masternodeSync.IsBlockchainSynced()) return strAutoDenomResult; switch(nState) { @@ -263,7 +306,7 @@ std::string CPrivateSendClient::GetStatus() if( nStatusMessageProgress % 70 <= 30) strSuffix = "."; else if(nStatusMessageProgress % 70 <= 50) strSuffix = ".."; else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; - return strprintf(_("Submitted to masternode, waiting in queue %s"), strSuffix);; + return strprintf(_("Submitted to masternode, waiting in queue %s"), strSuffix); case POOL_STATE_ACCEPTING_ENTRIES: if(nEntriesCount == 0) { nStatusMessageProgress = 0; @@ -296,25 +339,55 @@ std::string CPrivateSendClient::GetStatus() } } -bool CPrivateSendClient::GetMixingMasternodeInfo(masternode_info_t& mnInfoRet) +std::string CPrivateSendClientManager::GetStatuses() +{ + LOCK(cs_deqsessions); + std::string strStatus; + bool fWaitForBlock = WaitForAnotherBlock(); + + for (auto& session : deqSessions) { + strStatus += session.GetStatus(fWaitForBlock) + "; "; + } + return strStatus; +} + +std::string CPrivateSendClientManager::GetSessionDenoms() +{ + LOCK(cs_deqsessions); + std::string strSessionDenoms; + + for (auto& session : deqSessions) { + strSessionDenoms += (session.nSessionDenom ? CPrivateSend::GetDenominationsToString(session.nSessionDenom) : "N/A") + "; "; + } + return strSessionDenoms.empty() ? "N/A" : strSessionDenoms; +} + +bool CPrivateSendClientSession::GetMixingMasternodeInfo(masternode_info_t& mnInfoRet) const { mnInfoRet = infoMixingMasternode.fInfoValid ? infoMixingMasternode : masternode_info_t(); return infoMixingMasternode.fInfoValid; } -bool CPrivateSendClient::IsMixingMasternode(const CNode* pnode) +bool CPrivateSendClientManager::GetMixingMasternodesInfo(std::vector& vecMnInfoRet) const { - return infoMixingMasternode.fInfoValid && pnode->addr == infoMixingMasternode.addr; + LOCK(cs_deqsessions); + for (const auto& session : deqSessions) { + masternode_info_t mnInfo; + if (session.GetMixingMasternodeInfo(mnInfo)) { + vecMnInfoRet.push_back(mnInfo); + } + } + return !vecMnInfoRet.empty(); } // // Check the mixing progress and send client updates if a Masternode // -void CPrivateSendClient::CheckPool() +void CPrivateSendClientSession::CheckPool() { // reset if we're here for 10 seconds if((nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) && GetTime() - nTimeLastSuccessfulStep >= 10) { - LogPrint("privatesend", "CPrivateSendClient::CheckPool -- timeout, RESETTING\n"); + LogPrint("privatesend", "CPrivateSendClientSession::CheckPool -- timeout, RESETTING\n"); UnlockCoins(); if (nState == POOL_STATE_ERROR) { keyHolderStorage.ReturnAll(); @@ -326,24 +399,20 @@ void CPrivateSendClient::CheckPool() } // -// Check for various timeouts (queue objects, mixing, etc) +// Check session timeouts // -void CPrivateSendClient::CheckTimeout() +bool CPrivateSendClientSession::CheckTimeout() { - if(fMasternodeMode) return; - - CheckQueue(); - - if(!fEnablePrivateSend) return; + if(fMasternodeMode) return false; // catching hanging sessions switch(nState) { case POOL_STATE_ERROR: - LogPrint("privatesend", "CPrivateSendClient::CheckTimeout -- Pool error -- Running CheckPool\n"); + LogPrint("privatesend", "CPrivateSendClientSession::CheckTimeout -- Pool error -- Running CheckPool\n"); CheckPool(); break; case POOL_STATE_SUCCESS: - LogPrint("privatesend", "CPrivateSendClient::CheckTimeout -- Pool success -- Running CheckPool\n"); + LogPrint("privatesend", "CPrivateSendClientSession::CheckTimeout -- Pool success -- Running CheckPool\n"); CheckPool(); break; default: @@ -354,14 +423,34 @@ void CPrivateSendClient::CheckTimeout() int nTimeout = (nState == POOL_STATE_SIGNING) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT; bool fTimeout = GetTime() - nTimeLastSuccessfulStep >= nTimeout + nLagTime; - if(nState != POOL_STATE_IDLE && fTimeout) { - LogPrint("privatesend", "CPrivateSendClient::CheckTimeout -- %s timed out (%ds) -- resetting\n", - (nState == POOL_STATE_SIGNING) ? "Signing" : "Session", nTimeout); - UnlockCoins(); - keyHolderStorage.ReturnAll(); - SetNull(); - SetState(POOL_STATE_ERROR); - strLastMessage = _("Session timed out."); + if(nState == POOL_STATE_IDLE || !fTimeout) + return false; + + LogPrint("privatesend", "CPrivateSendClientSession::CheckTimeout -- %s timed out (%ds) -- resetting\n", + (nState == POOL_STATE_SIGNING) ? "Signing" : "Session", nTimeout); + UnlockCoins(); + keyHolderStorage.ReturnAll(); + SetNull(); + SetState(POOL_STATE_ERROR); + + return true; +} + +// +// Check all queues and sessions for timeouts +// +void CPrivateSendClientManager::CheckTimeout() +{ + if(fMasternodeMode) return; + if(!fEnablePrivateSend) return; + + CheckQueue(); + + LOCK(cs_deqsessions); + for (auto& session : deqSessions) { + if (session.CheckTimeout()) { + strAutoDenomResult = _("Session timed out."); + } } } @@ -369,10 +458,10 @@ void CPrivateSendClient::CheckTimeout() // Execute a mixing denomination via a Masternode. // This is only ran from clients // -bool CPrivateSendClient::SendDenominate(const std::vector& vecTxDSIn, const std::vector& vecTxOut, CConnman& connman) +bool CPrivateSendClientSession::SendDenominate(const std::vector< std::pair >& vecPSInOutPairsIn, CConnman& connman) { if(fMasternodeMode) { - LogPrintf("CPrivateSendClient::SendDenominate -- PrivateSend from a Masternode is not supported currently.\n"); + LogPrintf("CPrivateSendClientSession::SendDenominate -- PrivateSend from a Masternode is not supported currently.\n"); return false; } @@ -385,12 +474,12 @@ bool CPrivateSendClient::SendDenominate(const std::vector& vecTxDSIn, c for (const auto& txin : txMyCollateral.vin) vecOutPointLocked.push_back(txin.prevout); - for (const auto& txdsin : vecTxDSIn) - vecOutPointLocked.push_back(txdsin.prevout); + for (const auto& pair : vecPSInOutPairsIn) + vecOutPointLocked.push_back(pair.first.prevout); // we should already be connected to a Masternode if(!nSessionID) { - LogPrintf("CPrivateSendClient::SendDenominate -- No Masternode has been selected yet.\n"); + LogPrintf("CPrivateSendClientSession::SendDenominate -- No Masternode has been selected yet.\n"); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); @@ -401,45 +490,38 @@ bool CPrivateSendClient::SendDenominate(const std::vector& vecTxDSIn, c UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); - fEnablePrivateSend = false; - LogPrintf("CPrivateSendClient::SendDenominate -- Not enough disk space, disabling PrivateSend.\n"); + LogPrintf("CPrivateSendClientSession::SendDenominate -- Not enough disk space.\n"); return false; } SetState(POOL_STATE_ACCEPTING_ENTRIES); strLastMessage = ""; - LogPrintf("CPrivateSendClient::SendDenominate -- Added transaction to pool.\n"); - - { - // construct a pseudo tx, for debugging purpuses only - - CMutableTransaction tx; - - for (const auto& txdsin : vecTxDSIn) { - LogPrint("privatesend", "CPrivateSendClient::SendDenominate -- txdsin=%s\n", txdsin.ToString()); - tx.vin.push_back(txdsin); - } + LogPrintf("CPrivateSendClientSession::SendDenominate -- Added transaction to pool.\n"); - for (const CTxOut& txout : vecTxOut) { - LogPrint("privatesend", "CPrivateSendClient::SendDenominate -- txout=%s\n", txout.ToString()); - tx.vout.push_back(txout); - } + CMutableTransaction tx; // for debug purposes only + std::vector vecTxDSInTmp; + std::vector vecTxOutTmp; - LogPrintf("CPrivateSendClient::SendDenominate -- Submitting partial tx %s", tx.ToString()); + for (const auto& pair : vecPSInOutPairsIn) { + vecTxDSInTmp.emplace_back(pair.first); + vecTxOutTmp.emplace_back(pair.second); + tx.vin.emplace_back(pair.first); + tx.vout.emplace_back(pair.second); } + LogPrintf("CPrivateSendClientSession::SendDenominate -- Submitting partial tx %s", tx.ToString()); + // store our entry for later use - CDarkSendEntry entry(vecTxDSIn, vecTxOut, txMyCollateral); - vecEntries.push_back(entry); - RelayIn(entry, connman); + vecEntries.emplace_back(vecTxDSInTmp, vecTxOutTmp, txMyCollateral); + RelayIn(vecEntries.back(), connman); nTimeLastSuccessfulStep = GetTime(); return true; } // Incoming message from Masternode updating the progress of mixing -bool CPrivateSendClient::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew) +bool CPrivateSendClientSession::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew) { if(fMasternodeMode) return false; @@ -450,7 +532,7 @@ bool CPrivateSendClient::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesC // if rejected at any state if(nStatusUpdate == STATUS_REJECTED) { - LogPrintf("CPrivateSendClient::CheckPoolStateUpdate -- entry is rejected by Masternode\n"); + LogPrintf("CPrivateSendClientSession::CheckPoolStateUpdate -- entry is rejected by Masternode\n"); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); @@ -464,14 +546,14 @@ bool CPrivateSendClient::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesC // new session id should be set only in POOL_STATE_QUEUE state nSessionID = nSessionIDNew; nTimeLastSuccessfulStep = GetTime(); - LogPrintf("CPrivateSendClient::CheckPoolStateUpdate -- set nSessionID to %d\n", nSessionID); + LogPrintf("CPrivateSendClientSession::CheckPoolStateUpdate -- set nSessionID to %d\n", nSessionID); return true; } else if(nStateNew == POOL_STATE_ACCEPTING_ENTRIES && nEntriesCount != nEntriesCountNew) { nEntriesCount = nEntriesCountNew; nTimeLastSuccessfulStep = GetTime(); fLastEntryAccepted = true; - LogPrintf("CPrivateSendClient::CheckPoolStateUpdate -- new entry accepted!\n"); + LogPrintf("CPrivateSendClientSession::CheckPoolStateUpdate -- new entry accepted!\n"); return true; } } @@ -485,19 +567,22 @@ bool CPrivateSendClient::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesC // check it to make sure it's what we want, then sign it if we agree. // If we refuse to sign, it's possible we'll be charged collateral // -bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode, CConnman& connman) +bool CPrivateSendClientSession::SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode, CConnman& connman) { - if(fMasternodeMode || pnode == NULL) return false; + if (!pwalletMain) return false; + + if(fMasternodeMode || pnode == nullptr) return false; + if(!infoMixingMasternode.fInfoValid) return false; finalMutableTransaction = finalTransactionNew; - LogPrintf("CPrivateSendClient::SignFinalTransaction -- finalMutableTransaction=%s", finalMutableTransaction.ToString()); + LogPrintf("CPrivateSendClientSession::SignFinalTransaction -- finalMutableTransaction=%s", finalMutableTransaction.ToString()); // Make sure it's BIP69 compliant sort(finalMutableTransaction.vin.begin(), finalMutableTransaction.vin.end(), CompareInputBIP69()); sort(finalMutableTransaction.vout.begin(), finalMutableTransaction.vout.end(), CompareOutputBIP69()); if(finalMutableTransaction.GetHash() != finalTransactionNew.GetHash()) { - LogPrintf("CPrivateSendClient::SignFinalTransaction -- WARNING! Masternode %s is not BIP69 compliant!\n", infoMixingMasternode.outpoint.ToStringShort()); + LogPrintf("CPrivateSendClientSession::SignFinalTransaction -- WARNING! Masternode %s is not BIP69 compliant!\n", infoMixingMasternode.outpoint.ToStringShort()); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); @@ -543,7 +628,7 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti if(nFoundOutputsCount < nTargetOuputsCount || nValue1 != nValue2) { // in this case, something went wrong and we'll refuse to sign. It's possible we'll be charged collateral. But that's // better then signing if the transaction doesn't look like what we wanted. - LogPrintf("CPrivateSendClient::SignFinalTransaction -- My entries are not correct! Refusing to sign: nFoundOutputsCount: %d, nTargetOuputsCount: %d\n", nFoundOutputsCount, nTargetOuputsCount); + LogPrintf("CPrivateSendClientSession::SignFinalTransaction -- My entries are not correct! Refusing to sign: nFoundOutputsCount: %d, nTargetOuputsCount: %d\n", nFoundOutputsCount, nTargetOuputsCount); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); @@ -553,20 +638,20 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti const CKeyStore& keystore = *pwalletMain; - LogPrint("privatesend", "CPrivateSendClient::SignFinalTransaction -- Signing my input %i\n", nMyInputIndex); + LogPrint("privatesend", "CPrivateSendClientSession::SignFinalTransaction -- Signing my input %i\n", nMyInputIndex); if(!SignSignature(keystore, prevPubKey, finalMutableTransaction, nMyInputIndex, int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))) { // changes scriptSig - LogPrint("privatesend", "CPrivateSendClient::SignFinalTransaction -- Unable to sign my own transaction!\n"); + LogPrint("privatesend", "CPrivateSendClientSession::SignFinalTransaction -- Unable to sign my own transaction!\n"); // not sure what to do here, it will timeout...? } sigs.push_back(finalMutableTransaction.vin[nMyInputIndex]); - LogPrint("privatesend", "CPrivateSendClient::SignFinalTransaction -- nMyInputIndex: %d, sigs.size(): %d, scriptSig=%s\n", nMyInputIndex, (int)sigs.size(), ScriptToAsmStr(finalMutableTransaction.vin[nMyInputIndex].scriptSig)); + LogPrint("privatesend", "CPrivateSendClientSession::SignFinalTransaction -- nMyInputIndex: %d, sigs.size(): %d, scriptSig=%s\n", nMyInputIndex, (int)sigs.size(), ScriptToAsmStr(finalMutableTransaction.vin[nMyInputIndex].scriptSig)); } } } if(sigs.empty()) { - LogPrintf("CPrivateSendClient::SignFinalTransaction -- can't sign anything!\n"); + LogPrintf("CPrivateSendClientSession::SignFinalTransaction -- can't sign anything!\n"); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); @@ -575,7 +660,7 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti } // push all of our signatures to the Masternode - LogPrintf("CPrivateSendClient::SignFinalTransaction -- pushing sigs to the masternode, finalMutableTransaction=%s", finalMutableTransaction.ToString()); + LogPrintf("CPrivateSendClientSession::SignFinalTransaction -- pushing sigs to the masternode, finalMutableTransaction=%s", finalMutableTransaction.ToString()); CNetMsgMaker msgMaker(pnode->GetSendVersion()); connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSSIGNFINALTX, sigs)); SetState(POOL_STATE_SIGNING); @@ -585,13 +670,13 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti } // mixing transaction was completed (failed or successful) -void CPrivateSendClient::CompletedTransaction(PoolMessage nMessageID) +void CPrivateSendClientSession::CompletedTransaction(PoolMessage nMessageID) { if(fMasternodeMode) return; if(nMessageID == MSG_SUCCESS) { LogPrintf("CompletedTransaction -- success\n"); - nCachedLastSuccessBlock = nCachedBlockHeight; + privateSendClient.UpdatedSuccessBlock(); keyHolderStorage.KeepAll(); } else { LogPrintf("CompletedTransaction -- error\n"); @@ -602,12 +687,23 @@ void CPrivateSendClient::CompletedTransaction(PoolMessage nMessageID) strLastMessage = CPrivateSend::GetMessageByID(nMessageID); } -bool CPrivateSendClient::IsDenomSkipped(CAmount nDenomValue) +void CPrivateSendClientManager::UpdatedSuccessBlock() +{ + if(fMasternodeMode) return; + nCachedLastSuccessBlock = nCachedBlockHeight; +} + +bool CPrivateSendClientManager::IsDenomSkipped(const CAmount& nDenomValue) { return std::find(vecDenominationsSkipped.begin(), vecDenominationsSkipped.end(), nDenomValue) != vecDenominationsSkipped.end(); } -bool CPrivateSendClient::WaitForAnotherBlock() +void CPrivateSendClientManager::AddSkippedDenom(const CAmount& nDenomValue) +{ + vecDenominationsSkipped.push_back(nDenomValue); +} + +bool CPrivateSendClientManager::WaitForAnotherBlock() { if(!masternodeSync.IsMasternodeListSynced()) return true; @@ -618,11 +714,18 @@ bool CPrivateSendClient::WaitForAnotherBlock() return nCachedBlockHeight - nCachedLastSuccessBlock < nMinBlocksToWait; } -bool CPrivateSendClient::CheckAutomaticBackup() +bool CPrivateSendClientManager::CheckAutomaticBackup() { + if (!pwalletMain) { + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- Wallet is not initialized, no mixing available.\n"); + strAutoDenomResult = _("Wallet is not initialized") + ", " + _("no mixing available."); + fEnablePrivateSend = false; // no mixing + return false; + } + switch(nWalletBackups) { case 0: - LogPrint("privatesend", "CPrivateSendClient::CheckAutomaticBackup -- Automatic backups disabled, no mixing available.\n"); + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- Automatic backups disabled, no mixing available.\n"); strAutoDenomResult = _("Automatic backups disabled") + ", " + _("no mixing available."); fEnablePrivateSend = false; // stop mixing pwalletMain->nKeysLeftSinceAutoBackup = 0; // no backup, no "keys since last backup" @@ -631,43 +734,43 @@ bool CPrivateSendClient::CheckAutomaticBackup() // Automatic backup failed, nothing else we can do until user fixes the issue manually. // There is no way to bring user attention in daemon mode so we just update status and // keep spamming if debug is on. - LogPrint("privatesend", "CPrivateSendClient::CheckAutomaticBackup -- ERROR! Failed to create automatic backup.\n"); + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- ERROR! Failed to create automatic backup.\n"); strAutoDenomResult = _("ERROR! Failed to create automatic backup") + ", " + _("see debug.log for details."); return false; case -2: // We were able to create automatic backup but keypool was not replenished because wallet is locked. // There is no way to bring user attention in daemon mode so we just update status and // keep spamming if debug is on. - LogPrint("privatesend", "CPrivateSendClient::CheckAutomaticBackup -- WARNING! Failed to create replenish keypool, please unlock your wallet to do so.\n"); + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- WARNING! Failed to create replenish keypool, please unlock your wallet to do so.\n"); strAutoDenomResult = _("WARNING! Failed to replenish keypool, please unlock your wallet to do so.") + ", " + _("see debug.log for details."); return false; } if(pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_STOP) { // We should never get here via mixing itself but probably smth else is still actively using keypool - LogPrint("privatesend", "CPrivateSendClient::CheckAutomaticBackup -- Very low number of keys left: %d, no mixing available.\n", pwalletMain->nKeysLeftSinceAutoBackup); + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d, no mixing available.\n", pwalletMain->nKeysLeftSinceAutoBackup); strAutoDenomResult = strprintf(_("Very low number of keys left: %d") + ", " + _("no mixing available."), pwalletMain->nKeysLeftSinceAutoBackup); // It's getting really dangerous, stop mixing fEnablePrivateSend = false; return false; } else if(pwalletMain->nKeysLeftSinceAutoBackup < PRIVATESEND_KEYS_THRESHOLD_WARNING) { // Low number of keys left but it's still more or less safe to continue - LogPrint("privatesend", "CPrivateSendClient::CheckAutomaticBackup -- Very low number of keys left: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- Very low number of keys left: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); strAutoDenomResult = strprintf(_("Very low number of keys left: %d"), pwalletMain->nKeysLeftSinceAutoBackup); if(fCreateAutoBackups) { - LogPrint("privatesend", "CPrivateSendClient::CheckAutomaticBackup -- Trying to create new backup.\n"); + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- Trying to create new backup.\n"); std::string warningString; std::string errorString; if(!AutoBackupWallet(pwalletMain, "", warningString, errorString)) { if(!warningString.empty()) { // There were some issues saving backup but yet more or less safe to continue - LogPrintf("CPrivateSendClient::CheckAutomaticBackup -- WARNING! Something went wrong on automatic backup: %s\n", warningString); + LogPrintf("CPrivateSendClientManager::CheckAutomaticBackup -- WARNING! Something went wrong on automatic backup: %s\n", warningString); } if(!errorString.empty()) { // Things are really broken - LogPrintf("CPrivateSendClient::CheckAutomaticBackup -- ERROR! Failed to create automatic backup: %s\n", errorString); + LogPrintf("CPrivateSendClientManager::CheckAutomaticBackup -- ERROR! Failed to create automatic backup: %s\n", errorString); strAutoDenomResult = strprintf(_("ERROR! Failed to create automatic backup") + ": %s", errorString); return false; } @@ -678,7 +781,7 @@ bool CPrivateSendClient::CheckAutomaticBackup() } } - LogPrint("privatesend", "CPrivateSendClient::CheckAutomaticBackup -- Keys left since latest backup: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- Keys left since latest backup: %d\n", pwalletMain->nKeysLeftSinceAutoBackup); return true; } @@ -686,11 +789,9 @@ bool CPrivateSendClient::CheckAutomaticBackup() // // Passively run mixing in the background to anonymize funds based on the given configuration. // -bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun) +bool CPrivateSendClientSession::DoAutomaticDenominating(CConnman& connman, bool fDryRun) { if(fMasternodeMode) return false; // no client-side mixing on masternodes - if(!fEnablePrivateSend) return false; - if(!pwalletMain || pwalletMain->IsLocked(true)) return false; if(nState != POOL_STATE_IDLE) return false; if(!masternodeSync.IsMasternodeListSynced()) { @@ -698,8 +799,21 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun return false; } - if(!CheckAutomaticBackup()) + if (!pwalletMain) { + strAutoDenomResult = _("Wallet is not initialized"); return false; + } + + CAmount nBalanceNeedsAnonymized; + CAmount nValueMin = CPrivateSend::GetSmallestDenomination(); + + { + LOCK2(cs_main, pwalletMain->cs_wallet); + + if(!fDryRun && pwalletMain->IsLocked(true)) { + strAutoDenomResult = _("Wallet is locked."); + return false; + } if(GetEntriesCount() > 0) { strAutoDenomResult = _("Mixing in progress..."); @@ -712,25 +826,12 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun return false; } - if(!fDryRun && pwalletMain->IsLocked(true)) { - strAutoDenomResult = _("Wallet is locked."); - return false; - } - - if(WaitForAnotherBlock()) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- Last successful PrivateSend action was too recent\n"); - strAutoDenomResult = _("Last successful PrivateSend action was too recent."); - return false; - } - if(mnodeman.size() == 0) { - LogPrint("privatesend", "CPrivateSendClient::DoAutomaticDenominating -- No Masternodes detected\n"); + LogPrint("privatesend", "CPrivateSendClientSession::DoAutomaticDenominating -- No Masternodes detected\n"); strAutoDenomResult = _("No Masternodes detected."); return false; } - CAmount nValueMin = CPrivateSend::GetSmallestDenomination(); - // if there are no confirmed DS collateral inputs yet if(!pwalletMain->HasCollateralInputs()) { // should have some additional amount for them @@ -738,11 +839,11 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun } // including denoms but applying some restrictions - CAmount nBalanceNeedsAnonymized = pwalletMain->GetNeedsToBeAnonymizedBalance(nValueMin); + nBalanceNeedsAnonymized = pwalletMain->GetNeedsToBeAnonymizedBalance(nValueMin); // anonymizable balance is way too small if(nBalanceNeedsAnonymized < nValueMin) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- Not enough funds to anonymize\n"); + LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- Not enough funds to anonymize\n"); strAutoDenomResult = _("Not enough funds to anonymize."); return false; } @@ -754,7 +855,7 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun CAmount nBalanceDenominatedUnconf = pwalletMain->GetDenominatedBalance(true); CAmount nBalanceDenominated = nBalanceDenominatedConf + nBalanceDenominatedUnconf; - LogPrint("privatesend", "CPrivateSendClient::DoAutomaticDenominating -- nValueMin: %f, nBalanceNeedsAnonymized: %f, nBalanceAnonimizableNonDenom: %f, nBalanceDenominatedConf: %f, nBalanceDenominatedUnconf: %f, nBalanceDenominated: %f\n", + LogPrint("privatesend", "CPrivateSendClientSession::DoAutomaticDenominating -- nValueMin: %f, nBalanceNeedsAnonymized: %f, nBalanceAnonimizableNonDenom: %f, nBalanceDenominatedConf: %f, nBalanceDenominatedUnconf: %f, nBalanceDenominated: %f\n", (float)nValueMin/COIN, (float)nBalanceNeedsAnonymized/COIN, (float)nBalanceAnonimizableNonDenom/COIN, @@ -767,7 +868,7 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun // Check if we have should create more denominated inputs i.e. // there are funds to denominate and denominated balance does not exceed // max amount to mix yet. - if(nBalanceAnonimizableNonDenom >= nValueMin + CPrivateSend::GetCollateralAmount() && nBalanceDenominated < nPrivateSendAmount*COIN) + if(nBalanceAnonimizableNonDenom >= nValueMin + CPrivateSend::GetCollateralAmount() && nBalanceDenominated < privateSendClient.nPrivateSendAmount*COIN) return CreateDenominated(connman); //check if we have the collateral sized inputs @@ -786,8 +887,8 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun SetNull(); // should be no unconfirmed denoms in non-multi-session mode - if(!fPrivateSendMultiSession && nBalanceDenominatedUnconf > 0) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- Found unconfirmed denominated outputs, will wait till they confirm to continue.\n"); + if(!privateSendClient.fPrivateSendMultiSession && nBalanceDenominatedUnconf > 0) { + LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- Found unconfirmed denominated outputs, will wait till they confirm to continue.\n"); strAutoDenomResult = _("Found unconfirmed denominated outputs, will wait till they confirm to continue."); return false; } @@ -796,18 +897,54 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun std::string strReason; if(txMyCollateral == CMutableTransaction()) { if(!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- create collateral error:%s\n", strReason); + LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- create collateral error:%s\n", strReason); return false; } } else { if(!CPrivateSend::IsCollateralValid(txMyCollateral)) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- invalid collateral, recreating...\n"); + LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- invalid collateral, recreating...\n"); if(!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- create collateral error: %s\n", strReason); + LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- create collateral error: %s\n", strReason); return false; } } } + } // LOCK2(cs_main, pwalletMain->cs_wallet); + + bool fUseQueue = GetRandInt(100) > 33; + // don't use the queues all of the time for mixing unless we are a liquidity provider + if((privateSendClient.nLiquidityProvider || fUseQueue) && JoinExistingQueue(nBalanceNeedsAnonymized, connman)) + return true; + + // do not initiate queue if we are a liquidity provider to avoid useless inter-mixing + if(privateSendClient.nLiquidityProvider) return false; + + if(StartNewQueue(nValueMin, nBalanceNeedsAnonymized, connman)) + return true; + + strAutoDenomResult = _("No compatible Masternode found."); + return false; +} + +bool CPrivateSendClientManager::DoAutomaticDenominating(CConnman& connman, bool fDryRun) +{ + if (fMasternodeMode) return false; // no client-side mixing on masternodes + if (!fEnablePrivateSend) return false; + + if (!masternodeSync.IsMasternodeListSynced()) { + strAutoDenomResult = _("Can't mix while sync in progress."); + return false; + } + + if (!pwalletMain) { + strAutoDenomResult = _("Wallet is not initialized"); + return false; + } + + if (!fDryRun && pwalletMain->IsLocked(true)) { + strAutoDenomResult = _("Wallet is locked."); + return false; + } int nMnCountEnabled = mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION); @@ -821,36 +958,49 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun LogPrint("privatesend", " vecMasternodesUsed: new size: %d, threshold: %d\n", (int)vecMasternodesUsed.size(), nThreshold_high); } - bool fUseQueue = GetRandInt(100) > 33; - // don't use the queues all of the time for mixing unless we are a liquidity provider - if((nLiquidityProvider || fUseQueue) && JoinExistingQueue(nBalanceNeedsAnonymized, connman)) - return true; + LOCK(cs_deqsessions); + bool fResult = true; + if ((int)deqSessions.size() < nPrivateSendSessions) { + deqSessions.emplace_back(); + } + for (auto& session : deqSessions) { + if (!CheckAutomaticBackup()) + return false; - // do not initiate queue if we are a liquidity provider to avoid useless inter-mixing - if(nLiquidityProvider) return false; + if (WaitForAnotherBlock()) { + LogPrintf("CPrivateSendClientManager::DoAutomaticDenominating -- Last successful PrivateSend action was too recent\n"); + strAutoDenomResult = _("Last successful PrivateSend action was too recent."); + return false; + } - if(StartNewQueue(nValueMin, nBalanceNeedsAnonymized, connman)) - return true; + fResult &= session.DoAutomaticDenominating(connman, fDryRun); + } - strAutoDenomResult = _("No compatible Masternode found."); - return false; + return fResult; } -bool CPrivateSendClient::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman) +void CPrivateSendClientManager::AddUsedMasternode(const COutPoint& outpointMn) { - std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); - // Look through the queues and see if anything matches - for (auto& dsq : vecDarksendQueue) { - // only try each queue once - if(dsq.fTried) continue; - dsq.fTried = true; + vecMasternodesUsed.push_back(outpointMn); +} - if(dsq.IsExpired()) continue; +masternode_info_t CPrivateSendClientManager::GetNotUsedMasternode() +{ + return mnodeman.FindRandomNotInVec(vecMasternodesUsed, MIN_PRIVATESEND_PEER_PROTO_VERSION); +} +bool CPrivateSendClientSession::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman) +{ + if (!pwalletMain) return false; + + std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); + // Look through the queues and see if anything matches + CDarksendQueue dsq; + while (privateSendClient.GetQueueItemAndTry(dsq)) { masternode_info_t infoMn; if(!mnodeman.GetMasternodeInfo(dsq.masternodeOutpoint, infoMn)) { - LogPrintf("CPrivateSendClient::JoinExistingQueue -- dsq masternode is not in masternode list, masternode=%s\n", dsq.masternodeOutpoint.ToStringShort()); + LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- dsq masternode is not in masternode list, masternode=%s\n", dsq.masternodeOutpoint.ToStringShort()); continue; } @@ -858,7 +1008,7 @@ bool CPrivateSendClient::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CCon // skip next mn payments winners if (mnpayments.IsScheduled(infoMn, 0)) { - LogPrintf("CPrivateSendClient::JoinExistingQueue -- skipping winner, masternode=%s\n", infoMn.outpoint.ToStringShort()); + LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- skipping winner, masternode=%s\n", infoMn.outpoint.ToStringShort()); continue; } @@ -872,43 +1022,34 @@ bool CPrivateSendClient::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CCon // in order for dsq to get into vecDarksendQueue, so we should be safe to mix already, // no need for additional verification here - LogPrint("privatesend", "CPrivateSendClient::JoinExistingQueue -- found valid queue: %s\n", dsq.ToString()); + LogPrint("privatesend", "CPrivateSendClientSession::JoinExistingQueue -- found valid queue: %s\n", dsq.ToString()); - CAmount nValueInTmp = 0; - std::vector vecTxDSInTmp; - std::vector vCoinsTmp; + std::vector< std::pair > vecPSInOutPairsTmp; CAmount nMinAmount = vecStandardDenoms[vecBits.front()]; CAmount nMaxAmount = nBalanceNeedsAnonymized; - // nInputCount is not covered by legacy signature, require SPORK_6_NEW_SIGS to activate to use new algo - // (to make sure nInputCount wasn't modified by some intermediary node) - bool fNewAlgo = infoMn.nProtocolVersion > 70208 && sporkManager.IsSporkActive(SPORK_6_NEW_SIGS); - if (fNewAlgo && dsq.nInputCount != 0) { - nMinAmount = nMaxAmount = dsq.nInputCount * vecStandardDenoms[vecBits.front()]; - } // Try to match their denominations if possible, select exact number of denominations - if(!pwalletMain->SelectCoinsByDenominations(dsq.nDenom, nMinAmount, nMaxAmount, vecTxDSInTmp, vCoinsTmp, nValueInTmp, 0, nPrivateSendRounds)) { - LogPrintf("CPrivateSendClient::JoinExistingQueue -- Couldn't match %d denominations %d %d (%s)\n", dsq.nInputCount, vecBits.front(), dsq.nDenom, CPrivateSend::GetDenominationsToString(dsq.nDenom)); + if (!pwalletMain->SelectPSInOutPairsByDenominations(dsq.nDenom, nMinAmount, nMaxAmount, vecPSInOutPairsTmp)) { + LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- Couldn't match %d denominations %d (%s)\n", vecBits.front(), dsq.nDenom, CPrivateSend::GetDenominationsToString(dsq.nDenom)); continue; } - vecMasternodesUsed.push_back(dsq.masternodeOutpoint); + privateSendClient.AddUsedMasternode(dsq.masternodeOutpoint); if (connman.IsMasternodeOrDisconnectRequested(infoMn.addr)) { - LogPrintf("CPrivateSendClient::JoinExistingQueue -- skipping masternode connection, addr=%s\n", infoMn.addr.ToString()); + LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- skipping masternode connection, addr=%s\n", infoMn.addr.ToString()); continue; } nSessionDenom = dsq.nDenom; - nSessionInputCount = fNewAlgo ? dsq.nInputCount : 0; infoMixingMasternode = infoMn; - pendingDsaRequest = CPendingDsaRequest(infoMn.addr, CDarksendAccept(nSessionDenom, nSessionInputCount, txMyCollateral)); + pendingDsaRequest = CPendingDsaRequest(infoMn.addr, CDarksendAccept(nSessionDenom, txMyCollateral)); connman.AddPendingMasternode(infoMn.addr); // TODO: add new state POOL_STATE_CONNECTING and bump MIN_PRIVATESEND_PEER_PROTO_VERSION SetState(POOL_STATE_QUEUE); nTimeLastSuccessfulStep = GetTime(); - LogPrintf("CPrivateSendClient::JoinExistingQueue -- pending connection (from queue): nSessionDenom: %d (%s), nSessionInputCount: %d, addr=%s\n", - nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom), nSessionInputCount, infoMn.addr.ToString()); + LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- pending connection (from queue): nSessionDenom: %d (%s), addr=%s\n", + nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom), infoMn.addr.ToString()); strAutoDenomResult = _("Trying to connect..."); return true; } @@ -916,42 +1057,44 @@ bool CPrivateSendClient::JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CCon return false; } -bool CPrivateSendClient::StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsAnonymized, CConnman& connman) +bool CPrivateSendClientSession::StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsAnonymized, CConnman& connman) { + if (!pwalletMain) return false; + int nTries = 0; int nMnCountEnabled = mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION); // ** find the coins we'll use std::vector vecTxIn; CAmount nValueInTmp = 0; - if(!pwalletMain->SelectCoinsDark(nValueMin, nBalanceNeedsAnonymized, vecTxIn, nValueInTmp, 0, nPrivateSendRounds)) { + if(!pwalletMain->SelectCoinsDark(nValueMin, nBalanceNeedsAnonymized, vecTxIn, nValueInTmp, 0, privateSendClient.nPrivateSendRounds - 1)) { // this should never happen - LogPrintf("CPrivateSendClient::StartNewQueue -- Can't mix: no compatible inputs found!\n"); + LogPrintf("CPrivateSendClientSession::StartNewQueue -- Can't mix: no compatible inputs found!\n"); strAutoDenomResult = _("Can't mix: no compatible inputs found!"); return false; } // otherwise, try one randomly while(nTries < 10) { - masternode_info_t infoMn = mnodeman.FindRandomNotInVec(vecMasternodesUsed, MIN_PRIVATESEND_PEER_PROTO_VERSION); + masternode_info_t infoMn = privateSendClient.GetNotUsedMasternode(); if(!infoMn.fInfoValid) { - LogPrintf("CPrivateSendClient::StartNewQueue -- Can't find random masternode!\n"); + LogPrintf("CPrivateSendClientSession::StartNewQueue -- Can't find random masternode!\n"); strAutoDenomResult = _("Can't find random Masternode."); return false; } + privateSendClient.AddUsedMasternode(infoMn.outpoint); + // skip next mn payments winners if (mnpayments.IsScheduled(infoMn, 0)) { - LogPrintf("CPrivateSendClient::StartNewQueue -- skipping winner, masternode=%s\n", infoMn.outpoint.ToStringShort()); + LogPrintf("CPrivateSendClientSession::StartNewQueue -- skipping winner, masternode=%s\n", infoMn.outpoint.ToStringShort()); nTries++; continue; } - vecMasternodesUsed.push_back(infoMn.outpoint); - if(infoMn.nLastDsq != 0 && infoMn.nLastDsq + nMnCountEnabled/5 > mnodeman.nDsqCount) { - LogPrintf("CPrivateSendClient::StartNewQueue -- Too early to mix on this masternode!" + LogPrintf("CPrivateSendClientSession::StartNewQueue -- Too early to mix on this masternode!" " masternode=%s addr=%s nLastDsq=%d CountEnabled/5=%d nDsqCount=%d\n", infoMn.outpoint.ToStringShort(), infoMn.addr.ToString(), infoMn.nLastDsq, nMnCountEnabled/5, mnodeman.nDsqCount); @@ -960,12 +1103,12 @@ bool CPrivateSendClient::StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsA } if (connman.IsMasternodeOrDisconnectRequested(infoMn.addr)) { - LogPrintf("CPrivateSendClient::StartNewQueue -- skipping masternode connection, addr=%s\n", infoMn.addr.ToString()); + LogPrintf("CPrivateSendClientSession::StartNewQueue -- skipping masternode connection, addr=%s\n", infoMn.addr.ToString()); nTries++; continue; } - LogPrintf("CPrivateSendClient::StartNewQueue -- attempt %d connection to Masternode %s\n", nTries, infoMn.addr.ToString()); + LogPrintf("CPrivateSendClientSession::StartNewQueue -- attempt %d connection to Masternode %s\n", nTries, infoMn.addr.ToString()); std::vector vecAmounts; pwalletMain->ConvertList(vecTxIn, vecAmounts); @@ -974,37 +1117,14 @@ bool CPrivateSendClient::StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsA nSessionDenom = CPrivateSend::GetDenominationsByAmounts(vecAmounts); } - // Count available denominations. - // Should never really fail after this point, since we just selected compatible inputs ourselves. - std::vector vecBits; - if (!CPrivateSend::GetDenominationsBits(nSessionDenom, vecBits)) { - return false; - } - - CAmount nValueInTmp = 0; - std::vector vecTxDSInTmp; - std::vector vCoinsTmp; - std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); - - bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], vecStandardDenoms[vecBits.front()] * PRIVATESEND_ENTRY_MAX_SIZE, vecTxDSInTmp, vCoinsTmp, nValueInTmp, 0, nPrivateSendRounds); - if (!fSelected) { - return false; - } - - // nInputCount is not covered by legacy signature, require SPORK_6_NEW_SIGS to activate to use new algo - // (to make sure nInputCount wasn't modified by some intermediary node) - bool fNewAlgo = infoMn.nProtocolVersion > 70208 && sporkManager.IsSporkActive(SPORK_6_NEW_SIGS); - nSessionInputCount = fNewAlgo - ? std::min(vecTxDSInTmp.size(), size_t(5 + GetRand(PRIVATESEND_ENTRY_MAX_SIZE - 5 + 1))) - : 0; infoMixingMasternode = infoMn; connman.AddPendingMasternode(infoMn.addr); - pendingDsaRequest = CPendingDsaRequest(infoMn.addr, CDarksendAccept(nSessionDenom, nSessionInputCount, txMyCollateral)); + pendingDsaRequest = CPendingDsaRequest(infoMn.addr, CDarksendAccept(nSessionDenom, txMyCollateral)); // TODO: add new state POOL_STATE_CONNECTING and bump MIN_PRIVATESEND_PEER_PROTO_VERSION SetState(POOL_STATE_QUEUE); nTimeLastSuccessfulStep = GetTime(); - LogPrintf("CPrivateSendClient::StartNewQueue -- pending connection, nSessionDenom: %d (%s), nSessionInputCount: %d, addr=%s\n", - nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom), nSessionInputCount, infoMn.addr.ToString()); + LogPrintf("CPrivateSendClientSession::StartNewQueue -- pending connection, nSessionDenom: %d (%s), addr=%s\n", + nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom), infoMn.addr.ToString()); strAutoDenomResult = _("Trying to connect..."); return true; } @@ -1012,16 +1132,15 @@ bool CPrivateSendClient::StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsA return false; } -void CPrivateSendClient::ProcessPendingDsaRequest(CConnman& connman) +bool CPrivateSendClientSession::ProcessPendingDsaRequest(CConnman& connman) { - if (!pendingDsaRequest) return; + if (!pendingDsaRequest) return false; bool fDone = connman.ForNode(pendingDsaRequest.GetAddr(), [&](CNode* pnode) { LogPrint("privatesend", "-- processing dsa queue for addr=%s\n", pnode->addr.ToString()); nTimeLastSuccessfulStep = GetTime(); // TODO: this vvvv should be here after new state POOL_STATE_CONNECTING is added and MIN_PRIVATESEND_PEER_PROTO_VERSION is bumped // SetState(POOL_STATE_QUEUE); - strAutoDenomResult = _("Mixing in progress..."); CNetMsgMaker msgMaker(pnode->GetSendVersion()); connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSACCEPT, pendingDsaRequest.GetDSA())); return true; @@ -1030,53 +1149,82 @@ void CPrivateSendClient::ProcessPendingDsaRequest(CConnman& connman) if (fDone) { pendingDsaRequest = CPendingDsaRequest(); } else if (pendingDsaRequest.IsExpired()) { - LogPrint("privatesend", "CPrivateSendClient::%s -- failed to connect to %s\n", __func__, pendingDsaRequest.GetAddr().ToString()); + LogPrint("privatesend", "CPrivateSendClientSession::%s -- failed to connect to %s\n", __func__, pendingDsaRequest.GetAddr().ToString()); SetNull(); } + + return fDone; } -bool CPrivateSendClient::SubmitDenominate(CConnman& connman) +void CPrivateSendClientManager::ProcessPendingDsaRequest(CConnman& connman) { - std::string strError; - std::vector vecTxDSInRet; - std::vector vecTxOutRet; - - // Submit transaction to the pool if we get here - if (nLiquidityProvider) { - // Try to use only inputs with the same number of rounds starting from the lowest number of rounds possible - for(int i = 0; i< nPrivateSendRounds; i++) { - if(PrepareDenominate(i, i + 1, strError, vecTxDSInRet, vecTxOutRet)) { - LogPrintf("CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); - return SendDenominate(vecTxDSInRet, vecTxOutRet, connman); - } - LogPrint("privatesend", "CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); + LOCK(cs_deqsessions); + for (auto& session : deqSessions) { + if (session.ProcessPendingDsaRequest(connman)) { + strAutoDenomResult = _("Mixing in progress..."); } - } else { - // Try to use only inputs with the same number of rounds starting from the highest number of rounds possible - for(int i = nPrivateSendRounds; i > 0; i--) { - if(PrepareDenominate(i - 1, i, strError, vecTxDSInRet, vecTxOutRet)) { - LogPrintf("CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); - return SendDenominate(vecTxDSInRet, vecTxOutRet, connman); + } +} + +bool CPrivateSendClientSession::SubmitDenominate(CConnman& connman) +{ + LOCK2(cs_main, pwalletMain->cs_wallet); + + std::string strError; + std::vector< std::pair > vecPSInOutPairs, vecPSInOutPairsTmp; + + if (!SelectDenominate(strError, vecPSInOutPairs)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- SelectDenominate failed, error: %s\n", strError); + return false; + } + + std::vector< std::pair > vecInputsByRounds; + // Note: liquidity providers are fine with whatever number of inputs they've got + bool fDryRun = privateSendClient.nLiquidityProvider == 0; + + for (int i = 0; i < privateSendClient.nPrivateSendRounds; i++) { + if (PrepareDenominate(i, i, strError, vecPSInOutPairs, vecPSInOutPairsTmp, fDryRun)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", i); + if (!fDryRun) { + return SendDenominate(vecPSInOutPairsTmp, connman); } - LogPrint("privatesend", "CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); + vecInputsByRounds.emplace_back(i, vecPSInOutPairsTmp.size()); + } else { + LogPrint("privatesend", "CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, error: %s\n", i, strError); } } + // more inputs first, for equal input count prefer the one with less rounds + std::sort(vecInputsByRounds.begin(), vecInputsByRounds.end(), [](const auto& a, const auto& b) { + return a.second > b.second || (a.second == b.second && a.first < b.first); + }); + + LogPrint("privatesend", "vecInputsByRounds for denom %d\n", nSessionDenom); + for (const auto& pair : vecInputsByRounds) { + LogPrint("privatesend", "vecInputsByRounds: rounds: %d, inputs: %d\n", pair.first, pair.second); + } + + int nRounds = vecInputsByRounds.begin()->first; + if (PrepareDenominate(nRounds, nRounds, strError, vecPSInOutPairs, vecPSInOutPairsTmp)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for %d rounds, success\n", nRounds); + return SendDenominate(vecPSInOutPairsTmp, connman); + } + // We failed? That's strange but let's just make final attempt and try to mix everything - if(PrepareDenominate(0, nPrivateSendRounds, strError, vecTxDSInRet, vecTxOutRet)) { - LogPrintf("CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for all rounds, success\n"); - return SendDenominate(vecTxDSInRet, vecTxOutRet, connman); + if (PrepareDenominate(0, privateSendClient.nPrivateSendRounds - 1, strError, vecPSInOutPairs, vecPSInOutPairsTmp)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for all rounds, success\n"); + return SendDenominate(vecPSInOutPairsTmp, connman); } // Should never actually get here but just in case - LogPrintf("CPrivateSendClient::SubmitDenominate -- Running PrivateSend denominate for all rounds, error: %s\n", strError); + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- Running PrivateSend denominate for all rounds, error: %s\n", strError); strAutoDenomResult = strError; return false; } -bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector& vecTxDSInRet, std::vector& vecTxOutRet) +bool CPrivateSendClientSession::SelectDenominate(std::string& strErrorRet, std::vector< std::pair >& vecPSInOutPairsRet) { - if(!pwalletMain) { + if (!pwalletMain) { strErrorRet = "Wallet is not initialized"; return false; } @@ -1091,117 +1239,105 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std:: return false; } - // make sure returning vectors are empty before filling them up - vecTxDSInRet.clear(); - vecTxOutRet.clear(); + vecPSInOutPairsRet.clear(); - // ** find the coins we'll use - std::vector vecTxDSIn; - std::vector vCoins; - CAmount nValueIn = 0; - - /* - Select the coins we'll use - - if nMinRounds >= 0 it means only denominated inputs are going in and coming out - */ std::vector vecBits; if (!CPrivateSend::GetDenominationsBits(nSessionDenom, vecBits)) { strErrorRet = "Incorrect session denom"; return false; } std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); - bool fSelected = pwalletMain->SelectCoinsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], vecStandardDenoms[vecBits.front()] * PRIVATESEND_ENTRY_MAX_SIZE, vecTxDSIn, vCoins, nValueIn, nMinRounds, nMaxRounds); - if (nMinRounds >= 0 && !fSelected) { + + bool fSelected = pwalletMain->SelectPSInOutPairsByDenominations(nSessionDenom, vecStandardDenoms[vecBits.front()], CPrivateSend::GetMaxPoolAmount(), vecPSInOutPairsRet); + if (!fSelected) { strErrorRet = "Can't select current denominated inputs"; return false; } - LogPrintf("CPrivateSendClient::PrepareDenominate -- max value: %f\n", (double)nValueIn/COIN); + return true; +} - { - LOCK(pwalletMain->cs_wallet); - for (const auto& txin : vecTxDSIn) { - pwalletMain->LockCoin(txin.prevout); - } +bool CPrivateSendClientSession::PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair >& vecPSInOutPairsIn, std::vector< std::pair >& vecPSInOutPairsRet, bool fDryRun) +{ + std::vector vecBits; + if (!CPrivateSend::GetDenominationsBits(nSessionDenom, vecBits)) { + strErrorRet = "Incorrect session denom"; + return false; } - CAmount nValueLeft = nValueIn; + for (const auto& pair : vecPSInOutPairsIn) { + pwalletMain->LockCoin(pair.first.prevout); + } - // Try to add every needed denomination, repeat up to 5-PRIVATESEND_ENTRY_MAX_SIZE times. // NOTE: No need to randomize order of inputs because they were - // initially shuffled in CWallet::SelectCoinsByDenominations already. - int nStep = 0; - int nStepsMax = nSessionInputCount != 0 ? nSessionInputCount : (5 + GetRandInt(PRIVATESEND_ENTRY_MAX_SIZE - 5 + 1)); + // initially shuffled in CWallet::SelectPSInOutPairsByDenominations already. + int nDenomResult{0}; - while (nStep < nStepsMax) { + std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); + std::vector vecSteps(vecStandardDenoms.size(), 0); + vecPSInOutPairsRet.clear(); + + // Try to add up to PRIVATESEND_ENTRY_MAX_SIZE of every needed denomination + for (const auto& pair: vecPSInOutPairsIn) { + if (pair.second.nRounds < nMinRounds || pair.second.nRounds > nMaxRounds) { + // unlock unused coins + pwalletMain->UnlockCoin(pair.first.prevout); + continue; + } + bool fFound = false; for (const auto& nBit : vecBits) { + if (vecSteps[nBit] >= PRIVATESEND_ENTRY_MAX_SIZE) break; CAmount nValueDenom = vecStandardDenoms[nBit]; - if (nValueLeft - nValueDenom < 0) continue; - - // Note: this relies on a fact that both vectors MUST have same size - std::vector::iterator it = vecTxDSIn.begin(); - std::vector::iterator it2 = vCoins.begin(); - while (it2 != vCoins.end()) { - // we have matching inputs - if ((*it2).tx->tx->vout[(*it2).i].nValue == nValueDenom) { - // add new input in resulting vector - vecTxDSInRet.push_back(*it); - // remove corresponding items from initial vectors - vecTxDSIn.erase(it); - vCoins.erase(it2); - - CScript scriptDenom = keyHolderStorage.AddKey(pwalletMain); - - // add new output - CTxOut txout(nValueDenom, scriptDenom); - vecTxOutRet.push_back(txout); - - // subtract denomination amount - nValueLeft -= nValueDenom; - - // step is complete - break; + if (pair.second.nValue == nValueDenom) { + CScript scriptDenom; + if (fDryRun) { + scriptDenom = CScript(); + } else { + // randomly skip some inputs when we have at least one of the same denom already + // TODO: make it adjustable via options/cmd-line params + if (vecSteps[nBit] >= 1 && GetRandInt(5) == 0) { + // still count it as a step to randomize number of inputs + // if we have more than (or exactly) PRIVATESEND_ENTRY_MAX_SIZE of them + ++vecSteps[nBit]; + break; + } + scriptDenom = keyHolderStorage.AddKey(pwalletMain); } - ++it; - ++it2; + vecPSInOutPairsRet.emplace_back(pair.first, CTxOut(nValueDenom, scriptDenom)); + fFound = true; + nDenomResult |= 1 << nBit; + // step is complete + ++vecSteps[nBit]; + break; } } - nStep++; - if(nValueLeft == 0) break; - } - - { - // unlock unused coins - LOCK(pwalletMain->cs_wallet); - for (const auto& txin : vecTxDSIn) { - pwalletMain->UnlockCoin(txin.prevout); + if (!fFound || fDryRun) { + // unlock unused coins and if we are not going to mix right away + pwalletMain->UnlockCoin(pair.first.prevout); } } - if (CPrivateSend::GetDenominations(vecTxOutRet) != nSessionDenom || (nSessionInputCount != 0 && vecTxOutRet.size() != nSessionInputCount)) { - { - // unlock used coins on failure - LOCK(pwalletMain->cs_wallet); - for (const auto& txin : vecTxDSInRet) { - pwalletMain->UnlockCoin(txin.prevout); - } + if (nDenomResult != nSessionDenom) { + // unlock used coins on failure + for (const auto& pair : vecPSInOutPairsRet) { + pwalletMain->UnlockCoin(pair.first.prevout); } keyHolderStorage.ReturnAll(); - strErrorRet = "Can't make current denominated outputs"; + strErrorRet = "Can't prepare current denominated outputs"; return false; } - // We also do not care about full amount as long as we have right denominations return true; } // Create collaterals by looping through inputs grouped by addresses -bool CPrivateSendClient::MakeCollateralAmounts(CConnman& connman) +bool CPrivateSendClientSession::MakeCollateralAmounts(CConnman& connman) { + if (!pwalletMain) return false; + std::vector vecTally; - if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally, false)) { - LogPrint("privatesend", "CPrivateSendClient::MakeCollateralAmounts -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); + if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally, false, false)) { + LogPrint("privatesend", "CPrivateSendClientSession::MakeCollateralAmounts -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); return false; } @@ -1218,13 +1354,15 @@ bool CPrivateSendClient::MakeCollateralAmounts(CConnman& connman) } // If we got here then smth is terribly broken actually - LogPrintf("CPrivateSendClient::MakeCollateralAmounts -- ERROR: Can't make collaterals!\n"); + LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- ERROR: Can't make collaterals!\n"); return false; } // Split up large inputs or create fee sized inputs -bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated, CConnman& connman) +bool CPrivateSendClientSession::MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated, CConnman& connman) { + if (!pwalletMain) return false; + LOCK2(cs_main, pwalletMain->cs_wallet); // denominated input is always a single one, so we can check its amount directly and return early @@ -1261,13 +1399,13 @@ bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED); if(!fSuccess) { - LogPrintf("CPrivateSendClient::MakeCollateralAmounts -- ONLY_NONDENOMINATED: %s\n", strFail); + LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- ONLY_NONDENOMINATED: %s\n", strFail); // If we failed then most likely there are not enough funds on this address. if(fTryDenominated) { // Try to also use denominated coins (we can't mix denominated without collaterals anyway). if(!pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ALL_COINS)) { - LogPrintf("CPrivateSendClient::MakeCollateralAmounts -- ALL_COINS Error: %s\n", strFail); + LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- ALL_COINS Error: %s\n", strFail); reservekeyCollateral.ReturnKey(); return false; } @@ -1280,28 +1418,30 @@ bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem reservekeyCollateral.KeepKey(); - LogPrintf("CPrivateSendClient::MakeCollateralAmounts -- txid=%s\n", wtx.GetHash().GetHex()); + LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- txid=%s\n", wtx.GetHash().GetHex()); // use the same nCachedLastSuccessBlock as for DS mixing to prevent race CValidationState state; if(!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman, state)) { - LogPrintf("CPrivateSendClient::MakeCollateralAmounts -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); + LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); return false; } - nCachedLastSuccessBlock = nCachedBlockHeight; + privateSendClient.UpdatedSuccessBlock(); return true; } // Create denominations by looping through inputs grouped by addresses -bool CPrivateSendClient::CreateDenominated(CConnman& connman) +bool CPrivateSendClientSession::CreateDenominated(CConnman& connman) { + if (!pwalletMain) return false; + LOCK2(cs_main, pwalletMain->cs_wallet); std::vector vecTally; if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally)) { - LogPrint("privatesend", "CPrivateSendClient::CreateDenominated -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); + LogPrint("privatesend", "CPrivateSendClientSession::CreateDenominated -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); return false; } @@ -1312,20 +1452,22 @@ bool CPrivateSendClient::CreateDenominated(CConnman& connman) return true; } - LogPrintf("CPrivateSendClient::CreateDenominated -- failed!\n"); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- failed!\n"); return false; } // Create denominations -bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals, CConnman& connman) +bool CPrivateSendClientSession::CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals, CConnman& connman) { + if (!pwalletMain) return false; + std::vector vecSend; CKeyHolderStorage keyHolderStorageDenom; CAmount nValueLeft = tallyItem.nAmount; nValueLeft -= CPrivateSend::GetCollateralAmount(); // leave some room for fees - LogPrintf("CreateDenominated0: %s nValueLeft: %f\n", CBitcoinAddress(tallyItem.txdest).ToString(), (float)nValueLeft/COIN); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- 0 - %s nValueLeft: %f\n", CBitcoinAddress(tallyItem.txdest).ToString(), (float)nValueLeft/COIN); // ****** Add an output for mixing collaterals ************ / @@ -1343,20 +1485,25 @@ bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bo do { std::vector vecStandardDenoms = CPrivateSend::GetStandardDenominations(); - BOOST_REVERSE_FOREACH(CAmount nDenomValue, vecStandardDenoms) { + for (auto it = vecStandardDenoms.rbegin(); it != vecStandardDenoms.rend(); ++it) { + CAmount nDenomValue = *it; if(fSkip) { // Note: denoms are skipped if there are already DENOMS_COUNT_MAX of them // and there are still larger denoms which can be used for mixing // check skipped denoms - if(IsDenomSkipped(nDenomValue)) continue; + if(privateSendClient.IsDenomSkipped(nDenomValue)) { + strAutoDenomResult = strprintf(_("Too many %f denominations, skipping."), (float)nDenomValue/COIN); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- %s\n", strAutoDenomResult); + continue; + } // find new denoms to skip if any (ignore the largest one) if(nDenomValue != vecStandardDenoms.front() && pwalletMain->CountInputsWithAmount(nDenomValue) > DENOMS_COUNT_MAX) { strAutoDenomResult = strprintf(_("Too many %f denominations, removing."), (float)nDenomValue/COIN); - LogPrintf("CPrivateSendClient::CreateDenominated -- %s\n", strAutoDenomResult); - vecDenominationsSkipped.push_back(nDenomValue); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- %s\n", strAutoDenomResult); + privateSendClient.AddSkippedDenom(nDenomValue); continue; } } @@ -1372,17 +1519,20 @@ bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bo //increment outputs and subtract denomination amount nOutputs++; nValueLeft -= nDenomValue; - LogPrintf("CreateDenominated1: totalOutputs: %d, nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f\n", nOutputsTotal + nOutputs, nOutputsTotal, nOutputs, (float)nValueLeft/COIN); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- 1 - totalOutputs: %d, nOutputsTotal: %d, nOutputs: %d, nValueLeft: %f\n", nOutputsTotal + nOutputs, nOutputsTotal, nOutputs, (float)nValueLeft/COIN); } nOutputsTotal += nOutputs; if(nValueLeft == 0) break; } - LogPrintf("CreateDenominated2: nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft/COIN); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- 2 - nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft/COIN); // if there were no outputs added, start over without skipping fSkip = !fSkip; } while (nOutputsTotal == 0 && !fSkip); - LogPrintf("CreateDenominated3: nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft/COIN); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- 3 - nOutputsTotal: %d, nValueLeft: %f\n", nOutputsTotal, (float)nValueLeft/COIN); + + // No reasons to create mixing collaterals if we can't create denoms to mix + if (nOutputsTotal == 0) return false; // if we have anything left over, it will be automatically send back as change - there is no need to send it manually @@ -1404,7 +1554,7 @@ bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bo bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED); if(!fSuccess) { - LogPrintf("CPrivateSendClient::CreateDenominated -- Error: %s\n", strFail); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- Error: %s\n", strFail); keyHolderStorageDenom.ReturnAll(); return false; } @@ -1413,70 +1563,58 @@ bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bo CValidationState state; if(!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman, state)) { - LogPrintf("CPrivateSendClient::CreateDenominated -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); return false; } // use the same nCachedLastSuccessBlock as for DS mixing to prevent race - nCachedLastSuccessBlock = nCachedBlockHeight; - LogPrintf("CPrivateSendClient::CreateDenominated -- txid=%s\n", wtx.GetHash().GetHex()); + privateSendClient.UpdatedSuccessBlock(); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- txid=%s\n", wtx.GetHash().GetHex()); return true; } -void CPrivateSendClient::RelayIn(const CDarkSendEntry& entry, CConnman& connman) +void CPrivateSendClientSession::RelayIn(const CDarkSendEntry& entry, CConnman& connman) { if(!infoMixingMasternode.fInfoValid) return; connman.ForNode(infoMixingMasternode.addr, [&entry, &connman](CNode* pnode) { - LogPrintf("CPrivateSendClient::RelayIn -- found master, relaying message to %s\n", pnode->addr.ToString()); + LogPrintf("CPrivateSendClientSession::RelayIn -- found master, relaying message to %s\n", pnode->addr.ToString()); CNetMsgMaker msgMaker(pnode->GetSendVersion()); connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSVIN, entry)); return true; }); } -void CPrivateSendClient::SetState(PoolState nStateNew) +void CPrivateSendClientSession::SetState(PoolState nStateNew) { - LogPrintf("CPrivateSendClient::SetState -- nState: %d, nStateNew: %d\n", nState, nStateNew); + LogPrintf("CPrivateSendClientSession::SetState -- nState: %d, nStateNew: %d\n", nState, nStateNew); nState = nStateNew; } -void CPrivateSendClient::UpdatedBlockTip(const CBlockIndex *pindex) +void CPrivateSendClientManager::UpdatedBlockTip(const CBlockIndex *pindex) { nCachedBlockHeight = pindex->nHeight; - LogPrint("privatesend", "CPrivateSendClient::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight); + LogPrint("privatesend", "CPrivateSendClientManager::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight); } -//TODO: Rename/move to core -void ThreadCheckPrivateSendClient(CConnman& connman) +void CPrivateSendClientManager::DoMaintenance(CConnman& connman) { if(fLiteMode) return; // disable all absolute specific functionality if(fMasternodeMode) return; // no client-side mixing on masternodes - static bool fOneThread; - if(fOneThread) return; - fOneThread = true; - - // Make this thread recognisable as the PrivateSend thread - RenameThread("absolute-ps-client"); + if(!masternodeSync.IsBlockchainSynced() || ShutdownRequested()) + return; - unsigned int nTick = 0; - unsigned int nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN; + static unsigned int nTick = 0; + static unsigned int nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN; - while (true) - { - MilliSleep(1000); - - if(masternodeSync.IsBlockchainSynced() && !ShutdownRequested()) { - nTick++; - privateSendClient.CheckTimeout(); - privateSendClient.ProcessPendingDsaRequest(connman); - if(nDoAutoNextRun == nTick) { - privateSendClient.DoAutomaticDenominating(connman); - nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + GetRandInt(PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN); - } - } + nTick++; + CheckTimeout(); + ProcessPendingDsaRequest(connman); + if(nDoAutoNextRun == nTick) { + DoAutomaticDenominating(connman); + nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + GetRandInt(PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN); } } diff --git a/src/privatesend-client.h b/src/privatesend-client.h index 170b19153d6f..fb49a4aeb866 100644 --- a/src/privatesend-client.h +++ b/src/privatesend-client.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -11,18 +11,21 @@ #include "wallet/wallet.h" #include "privatesend-util.h" -class CPrivateSendClient; +class CPrivateSendClientManager; class CConnman; static const int DENOMS_COUNT_MAX = 100; +static const int MIN_PRIVATESEND_SESSIONS = 1; static const int MIN_PRIVATESEND_ROUNDS = 2; static const int MIN_PRIVATESEND_AMOUNT = 2; static const int MIN_PRIVATESEND_LIQUIDITY = 0; +static const int MAX_PRIVATESEND_SESSIONS = 10; static const int MAX_PRIVATESEND_ROUNDS = 16; static const int MAX_PRIVATESEND_AMOUNT = MAX_MONEY / COIN; static const int MAX_PRIVATESEND_LIQUIDITY = 100; -static const int DEFAULT_PRIVATESEND_ROUNDS = 2; +static const int DEFAULT_PRIVATESEND_SESSIONS = 4; +static const int DEFAULT_PRIVATESEND_ROUNDS = 4; static const int DEFAULT_PRIVATESEND_AMOUNT = 1000; static const int DEFAULT_PRIVATESEND_LIQUIDITY = 0; @@ -35,7 +38,7 @@ static const int PRIVATESEND_KEYS_THRESHOLD_WARNING = 100; static const int PRIVATESEND_KEYS_THRESHOLD_STOP = 50; // The main object for accessing mixing -extern CPrivateSendClient privateSendClient; +extern CPrivateSendClientManager privateSendClient; class CPendingDsaRequest { @@ -51,12 +54,13 @@ class CPendingDsaRequest addr(CService()), dsa(CDarksendAccept()), nTimeCreated(0) - {}; + {} CPendingDsaRequest(const CService& addr_, const CDarksendAccept& dsa_): addr(addr_), - dsa(dsa_) - { nTimeCreated = GetTime(); } + dsa(dsa_), + nTimeCreated(GetTime()) + {} CService GetAddr() { return addr; } CDarksendAccept GetDSA() { return dsa; } @@ -76,23 +80,11 @@ class CPendingDsaRequest } }; -/** Used to keep track of current status of mixing pool - */ -class CPrivateSendClient : public CPrivateSendBase +class CPrivateSendClientSession : public CPrivateSendBaseSession { private: - // Keep track of the used Masternodes - std::vector vecMasternodesUsed; - - std::vector vecDenominationsSkipped; std::vector vecOutPointLocked; - int nCachedLastSuccessBlock; - int nMinBlocksToWait; // how many blocks to wait after one successful mixing tx in non-multisession mode - - // Keep track of current block height - int nCachedBlockHeight; - int nEntriesCount; bool fLastEntryAccepted; @@ -105,19 +97,6 @@ class CPrivateSendClient : public CPrivateSendBase CKeyHolderStorage keyHolderStorage; // storage for keys used in PrepareDenominate - /// Check for process - void CheckPool(); - void CompletedTransaction(PoolMessage nMessageID); - - bool IsDenomSkipped(CAmount nDenomValue); - - bool WaitForAnotherBlock(); - - // Make sure we have enough keys since last backup - bool CheckAutomaticBackup(); - bool JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman); - bool StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsAnonymized, CConnman& connman); - /// Create denominations bool CreateDenominated(CConnman& connman); bool CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals, CConnman& connman); @@ -126,18 +105,25 @@ class CPrivateSendClient : public CPrivateSendBase bool MakeCollateralAmounts(CConnman& connman); bool MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated, CConnman& connman); - /// As a client, submit part of a future mixing transaction to a Masternode to start the process - bool SubmitDenominate(CConnman& connman); + bool JoinExistingQueue(CAmount nBalanceNeedsAnonymized, CConnman& connman); + bool StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsAnonymized, CConnman& connman); + + /// step 0: select denominated inputs and txouts + bool SelectDenominate(std::string& strErrorRet, std::vector< std::pair >& vecPSInOutPairsRet); /// step 1: prepare denominated inputs and outputs - bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, std::vector& vecTxDSInRet, std::vector& vecTxOutRet); + bool PrepareDenominate(int nMinRounds, int nMaxRounds, std::string& strErrorRet, const std::vector< std::pair >& vecPSInOutPairsIn, std::vector< std::pair >& vecPSInOutPairsRet, bool fDryRun = false); /// step 2: send denominated inputs and outputs prepared in step 1 - bool SendDenominate(const std::vector& vecTxDSIn, const std::vector& vecTxOut, CConnman& connman); + bool SendDenominate(const std::vector< std::pair >& vecPSInOutPairsIn, CConnman& connman); /// Get Masternode updates about the progress of mixing bool CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew=0); // Set the 'state' value, with some logging and capturing when the state changed void SetState(PoolState nStateNew); + /// Check for process + void CheckPool(); + void CompletedTransaction(PoolMessage nMessageID); + /// As a client, check and sign the final transaction bool SignFinalTransaction(const CTransaction& finalTransactionNew, CNode* pnode, CConnman& connman); @@ -146,6 +132,67 @@ class CPrivateSendClient : public CPrivateSendBase void SetNull(); public: + CPrivateSendClientSession() : + vecOutPointLocked(), + nEntriesCount(0), + fLastEntryAccepted(false), + strLastMessage(), + strAutoDenomResult(), + infoMixingMasternode(), + txMyCollateral(), + pendingDsaRequest(), + keyHolderStorage() + {} + + void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); + + void UnlockCoins(); + + void ResetPool(); + + std::string GetStatus(bool fWaitForBlock); + + bool GetMixingMasternodeInfo(masternode_info_t& mnInfoRet) const; + + /// Passively run mixing in the background according to the configuration in settings + bool DoAutomaticDenominating(CConnman& connman, bool fDryRun=false); + + /// As a client, submit part of a future mixing transaction to a Masternode to start the process + bool SubmitDenominate(CConnman& connman); + + bool ProcessPendingDsaRequest(CConnman& connman); + + bool CheckTimeout(); +}; + +/** Used to keep track of current status of mixing pool + */ +class CPrivateSendClientManager : public CPrivateSendBaseManager +{ +private: + // Keep track of the used Masternodes + std::vector vecMasternodesUsed; + + std::vector vecDenominationsSkipped; + + // TODO: or map ?? + std::deque deqSessions; + mutable CCriticalSection cs_deqsessions; + + int nCachedLastSuccessBlock; + int nMinBlocksToWait; // how many blocks to wait after one successful mixing tx in non-multisession mode + std::string strAutoDenomResult; + + // Keep track of current block height + int nCachedBlockHeight; + + bool WaitForAnotherBlock(); + + // Make sure we have enough keys since last backup + bool CheckAutomaticBackup(); + +public: + int nPrivateSendSessions; int nPrivateSendRounds; int nPrivateSendAmount; int nLiquidityProvider; @@ -155,44 +202,53 @@ class CPrivateSendClient : public CPrivateSendBase int nCachedNumBlocks; //used for the overview screen bool fCreateAutoBackups; //builtin support for automatic backups - CPrivateSendClient() : + CPrivateSendClientManager() : + vecMasternodesUsed(), + vecDenominationsSkipped(), + deqSessions(), nCachedLastSuccessBlock(0), nMinBlocksToWait(1), - txMyCollateral(CMutableTransaction()), + strAutoDenomResult(), + nCachedBlockHeight(0), nPrivateSendRounds(DEFAULT_PRIVATESEND_ROUNDS), nPrivateSendAmount(DEFAULT_PRIVATESEND_AMOUNT), nLiquidityProvider(DEFAULT_PRIVATESEND_LIQUIDITY), fEnablePrivateSend(false), fPrivateSendMultiSession(DEFAULT_PRIVATESEND_MULTISESSION), nCachedNumBlocks(std::numeric_limits::max()), - fCreateAutoBackups(true) { SetNull(); } + fCreateAutoBackups(true) + {} void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); + bool IsDenomSkipped(const CAmount& nDenomValue); + void AddSkippedDenom(const CAmount& nDenomValue); void ClearSkippedDenominations() { vecDenominationsSkipped.clear(); } void SetMinBlocksToWait(int nMinBlocksToWaitIn) { nMinBlocksToWait = nMinBlocksToWaitIn; } - void ResetPool(); - void UnlockCoins(); + std::string GetStatuses(); + std::string GetSessionDenoms(); - std::string GetStatus(); - - bool GetMixingMasternodeInfo(masternode_info_t& mnInfoRet); - bool IsMixingMasternode(const CNode* pnode); + bool GetMixingMasternodesInfo(std::vector& vecMnInfoRet) const; /// Passively run mixing in the background according to the configuration in settings bool DoAutomaticDenominating(CConnman& connman, bool fDryRun=false); + void CheckTimeout(); + void ProcessPendingDsaRequest(CConnman& connman); - void CheckTimeout(); + void AddUsedMasternode(const COutPoint& outpointMn); + masternode_info_t GetNotUsedMasternode(); + + void UpdatedSuccessBlock(); void UpdatedBlockTip(const CBlockIndex *pindex); -}; -void ThreadCheckPrivateSendClient(CConnman& connman); + void DoMaintenance(CConnman& connman); +}; #endif diff --git a/src/privatesend-server.cpp b/src/privatesend-server.cpp index f11301cffb5a..8ee23b83a8a3 100644 --- a/src/privatesend-server.cpp +++ b/src/privatesend-server.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "privatesend-server.h" @@ -46,10 +46,8 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm LogPrint("privatesend", "DSACCEPT -- nDenom %d (%s) txCollateral %s", dsa.nDenom, CPrivateSend::GetDenominationsToString(dsa.nDenom), dsa.txCollateral.ToString()); - if(dsa.nInputCount < 0 || dsa.nInputCount > PRIVATESEND_ENTRY_MAX_SIZE) return; - masternode_info_t mnInfo; - if(!mnodeman.GetMasternodeInfo(activeMasternode.outpoint, mnInfo)) { + if(!mnodeman.GetMasternodeInfo(activeMasternodeInfo.outpoint, mnInfo)) { PushStatus(pfrom, STATUS_REJECTED, ERR_MN_LIST, connman); return; } @@ -77,7 +75,7 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm } } else if(strCommand == NetMsgType::DSQUEUE) { - TRY_LOCK(cs_darksend, lockRecv); + TRY_LOCK(cs_vecqueue, lockRecv); if(!lockRecv) return; if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { @@ -101,12 +99,11 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm LogPrint("privatesend", "DSQUEUE -- %s new\n", dsq.ToString()); if(dsq.IsExpired()) return; - if(dsq.nInputCount < 0 || dsq.nInputCount > PRIVATESEND_ENTRY_MAX_SIZE) return; masternode_info_t mnInfo; if(!mnodeman.GetMasternodeInfo(dsq.masternodeOutpoint, mnInfo)) return; - if(!dsq.CheckSignature(mnInfo.pubKeyMasternode)) { + if(!dsq.CheckSignature(mnInfo.legacyKeyIDOperator, mnInfo.blsPubKeyOperator)) { // we probably have outdated info mnodeman.AskForMN(pfrom, dsq.masternodeOutpoint, connman); return; @@ -169,18 +166,6 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm return; } - if(nSessionInputCount != 0 && entry.vecTxDSIn.size() != nSessionInputCount) { - LogPrintf("DSVIN -- ERROR: incorrect number of inputs! %d/%d\n", entry.vecTxDSIn.size(), nSessionInputCount); - PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_INPUT_COUNT, connman); - return; - } - - if(nSessionInputCount != 0 && entry.vecTxOut.size() != nSessionInputCount) { - LogPrintf("DSVIN -- ERROR: incorrect number of outputs! %d/%d\n", entry.vecTxOut.size(), nSessionInputCount); - PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_INPUT_COUNT, connman); - return; - } - //do we have the same denominations as the current session? if(!IsOutputsCompatibleWithSessionDenom(entry.vecTxOut)) { LogPrintf("DSVIN -- not compatible with existing transactions!\n"); @@ -283,7 +268,8 @@ void CPrivateSendServer::SetNull() // MN side vecSessionCollaterals.clear(); - CPrivateSendBase::SetNull(); + CPrivateSendBaseSession::SetNull(); + CPrivateSendBaseManager::SetNull(); } // @@ -350,7 +336,7 @@ void CPrivateSendServer::CommitFinalTransaction(CConnman& connman) TRY_LOCK(cs_main, lockMain); CValidationState validationState; mempool.PrioritiseTransaction(hashTx, hashTx.ToString(), 1000, 0.1*COIN); - if(!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, false, NULL, NULL, false, maxTxFee, true)) + if(!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, false, NULL, false, maxTxFee, true)) { LogPrintf("CPrivateSendServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n"); SetNull(); @@ -364,7 +350,7 @@ void CPrivateSendServer::CommitFinalTransaction(CConnman& connman) // create and sign masternode dstx transaction if(!CPrivateSend::GetDSTX(hashTx)) { - CDarksendBroadcastTx dstxNew(finalTransaction, activeMasternode.outpoint, GetAdjustedTime()); + CDarksendBroadcastTx dstxNew(finalTransaction, activeMasternodeInfo.outpoint, GetAdjustedTime()); dstxNew.Sign(); CPrivateSend::AddDSTX(dstxNew); } @@ -446,13 +432,13 @@ void CPrivateSendServer::ChargeFees(CConnman& connman) std::random_shuffle(vecOffendersCollaterals.begin(), vecOffendersCollaterals.end()); if(nState == POOL_STATE_ACCEPTING_ENTRIES || nState == POOL_STATE_SIGNING) { - LogPrintf("CPrivateSendServer::ChargeFees -- found uncooperative node (didn't %s transaction), charging fees: %s\n", + LogPrintf("CPrivateSendServer::ChargeFees -- found uncooperative node (didn't %s transaction), charging fees: %s", (nState == POOL_STATE_SIGNING) ? "sign" : "send", vecOffendersCollaterals[0]->ToString()); LOCK(cs_main); CValidationState state; - if(!AcceptToMemoryPool(mempool, state, vecOffendersCollaterals[0], false, NULL, NULL, false, maxTxFee)) { + if(!AcceptToMemoryPool(mempool, state, vecOffendersCollaterals[0], false, NULL, false, maxTxFee)) { // should never really happen LogPrintf("CPrivateSendServer::ChargeFees -- ERROR: AcceptToMemoryPool failed!\n"); } else { @@ -484,7 +470,7 @@ void CPrivateSendServer::ChargeRandomFees(CConnman& connman) LogPrintf("CPrivateSendServer::ChargeRandomFees -- charging random fees, txCollateral=%s", txCollateral->ToString()); CValidationState state; - if(!AcceptToMemoryPool(mempool, state, txCollateral, false, NULL, NULL, false, maxTxFee)) { + if(!AcceptToMemoryPool(mempool, state, txCollateral, false, NULL, false, maxTxFee)) { // should never really happen LogPrintf("CPrivateSendServer::ChargeRandomFees -- ERROR: AcceptToMemoryPool failed!\n"); } else { @@ -525,7 +511,7 @@ void CPrivateSendServer::CheckForCompleteQueue(CConnman& connman) if(nState == POOL_STATE_QUEUE && IsSessionReady()) { SetState(POOL_STATE_ACCEPTING_ENTRIES); - CDarksendQueue dsq(nSessionDenom, nSessionInputCount, activeMasternode.outpoint, GetAdjustedTime(), true); + CDarksendQueue dsq(nSessionDenom, activeMasternodeInfo.outpoint, GetAdjustedTime(), true); LogPrint("privatesend", "CPrivateSendServer::CheckForCompleteQueue -- queue is ready, signing and relaying (%s)\n", dsq.ToString()); dsq.Sign(); dsq.Relay(connman); @@ -703,12 +689,6 @@ bool CPrivateSendServer::IsAcceptableDSA(const CDarksendAccept& dsa, PoolMessage return false; } - if(dsa.nInputCount < 0 || dsa.nInputCount > PRIVATESEND_ENTRY_MAX_SIZE) { - LogPrint("privatesend", "CPrivateSendServer::%s -- requested count is not valid!\n", __func__); - nMessageIDRet = ERR_INVALID_INPUT_COUNT; - return false; - } - return true; } @@ -731,16 +711,13 @@ bool CPrivateSendServer::CreateNewSession(const CDarksendAccept& dsa, PoolMessag nMessageIDRet = MSG_NOERR; nSessionID = GetRandInt(999999)+1; nSessionDenom = dsa.nDenom; - // nInputCount is not covered by legacy signature, require SPORK_6_NEW_SIGS to activate to use new algo - // (to make sure nInputCount wasn't modified by some intermediary node) - nSessionInputCount = sporkManager.IsSporkActive(SPORK_6_NEW_SIGS) ? dsa.nInputCount : 0; SetState(POOL_STATE_QUEUE); nTimeLastSuccessfulStep = GetTime(); if(!fUnitTest) { //broadcast that I'm accepting entries, only if it's the first entry through - CDarksendQueue dsq(dsa.nDenom, dsa.nInputCount, activeMasternode.outpoint, GetAdjustedTime(), false); + CDarksendQueue dsq(nSessionDenom, activeMasternodeInfo.outpoint, GetAdjustedTime(), false); LogPrint("privatesend", "CPrivateSendServer::CreateNewSession -- signing and relaying new queue: %s\n", dsq.ToString()); dsq.Sign(); dsq.Relay(connman); @@ -776,21 +753,14 @@ bool CPrivateSendServer::AddUserToExistingSession(const CDarksendAccept& dsa, Po return false; } - if(dsa.nInputCount != nSessionInputCount) { - LogPrintf("CPrivateSendServer::AddUserToExistingSession -- incompatible count %d != nSessionInputCount %d\n", - dsa.nInputCount, nSessionInputCount); - nMessageIDRet = ERR_INVALID_INPUT_COUNT; - return false; - } - // count new user as accepted to an existing session nMessageIDRet = MSG_NOERR; nTimeLastSuccessfulStep = GetTime(); vecSessionCollaterals.push_back(MakeTransactionRef(dsa.txCollateral)); - LogPrintf("CPrivateSendServer::AddUserToExistingSession -- new user accepted, nSessionID: %d nSessionDenom: %d (%s) nSessionInputCount: %d vecSessionCollaterals.size(): %d\n", - nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom), nSessionInputCount, vecSessionCollaterals.size()); + LogPrintf("CPrivateSendServer::AddUserToExistingSession -- new user accepted, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d\n", + nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); return true; } @@ -891,29 +861,14 @@ void CPrivateSendServer::SetState(PoolState nStateNew) nState = nStateNew; } -//TODO: Rename/move to core -void ThreadCheckPrivateSendServer(CConnman& connman) +void CPrivateSendServer::DoMaintenance(CConnman& connman) { - if(fLiteMode) return; // disable all Dash specific functionality + if(fLiteMode) return; // disable all Absolute specific functionality if(!fMasternodeMode) return; // only run on masternodes - static bool fOneThread; - if(fOneThread) return; - fOneThread = true; - - // Make this thread recognisable as the PrivateSend thread - RenameThread("absolute-ps-server"); - - unsigned int nTick = 0; - - while (true) - { - MilliSleep(1000); + if(!masternodeSync.IsBlockchainSynced() || ShutdownRequested()) + return; - if(masternodeSync.IsBlockchainSynced() && !ShutdownRequested()) { - nTick++; - privateSendServer.CheckTimeout(connman); - privateSendServer.CheckForCompleteQueue(connman); - } - } + privateSendServer.CheckTimeout(connman); + privateSendServer.CheckForCompleteQueue(connman); } diff --git a/src/privatesend-server.h b/src/privatesend-server.h index aa9909fafca9..dae23c9e7d73 100644 --- a/src/privatesend-server.h +++ b/src/privatesend-server.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -16,7 +16,7 @@ extern CPrivateSendServer privateSendServer; /** Used to keep track of current status of mixing pool */ -class CPrivateSendServer : public CPrivateSendBase +class CPrivateSendServer : public CPrivateSendBaseSession, public CPrivateSendBaseManager { private: // Mixing uses collateral transactions to trust parties entering the pool @@ -67,15 +67,14 @@ class CPrivateSendServer : public CPrivateSendBase void SetNull(); public: - CPrivateSendServer() : - fUnitTest(false) { SetNull(); } + CPrivateSendServer() : vecSessionCollaterals(), fUnitTest(false) {} void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); void CheckTimeout(CConnman& connman); void CheckForCompleteQueue(CConnman& connman); -}; -void ThreadCheckPrivateSendServer(CConnman& connman); + void DoMaintenance(CConnman& connman); +}; #endif diff --git a/src/privatesend-util.cpp b/src/privatesend-util.cpp index 62a43e891fb8..2aaa41de5b3c 100644 --- a/src/privatesend-util.cpp +++ b/src/privatesend-util.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "privatesend-util.h" diff --git a/src/privatesend-util.h b/src/privatesend-util.h index c673951f8638..600bf0966c73 100644 --- a/src/privatesend-util.h +++ b/src/privatesend-util.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/privatesend.cpp b/src/privatesend.cpp index 5f9da4d763b3..0dc0b10d370c 100644 --- a/src/privatesend.cpp +++ b/src/privatesend.cpp @@ -1,26 +1,22 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "privatesend.h" #include "activemasternode.h" #include "consensus/validation.h" -#include "governance.h" -#include "init.h" -#include "instantx.h" #include "masternode-payments.h" #include "masternode-sync.h" #include "masternodeman.h" #include "messagesigner.h" -#include "netfulfilledman.h" #include "netmessagemaker.h" #include "script/sign.h" #include "txmempool.h" #include "util.h" #include "utilmoneystr.h" -#include +#include bool CDarkSendEntry::AddScriptSig(const CTxIn& txin) { @@ -40,7 +36,14 @@ bool CDarkSendEntry::AddScriptSig(const CTxIn& txin) uint256 CDarksendQueue::GetSignatureHash() const { - return SerializeHash(*this); + // Remove after migration to 70211 + { + masternode_info_t mnInfo; + mnodeman.GetMasternodeInfo(masternodeOutpoint, mnInfo); + return SerializeHash(*this, SER_GETHASH, mnInfo.nProtocolVersion); + } + // END remove, replace with the code below + // return SerializeHash(*this); } bool CDarksendQueue::Sign() @@ -49,30 +52,34 @@ bool CDarksendQueue::Sign() std::string strError = ""; - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + uint256 hash = GetSignatureHash(); + CBLSSignature sig = activeMasternodeInfo.blsKeyOperator->Sign(hash); + sig.GetBuf(vchSig); + } else if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::SignHash(hash, activeMasternode.keyMasternode, vchSig)) { + if (!CHashSigner::SignHash(hash, activeMasternodeInfo.legacyKeyOperator, vchSig)) { LogPrintf("CDarksendQueue::Sign -- SignHash() failed\n"); return false; } - if (!CHashSigner::VerifyHash(hash, activeMasternode.pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, activeMasternodeInfo.legacyKeyIDOperator, vchSig, strError)) { LogPrintf("CDarksendQueue::Sign -- VerifyHash() failed, error: %s\n", strError); return false; } } else { std::string strMessage = CTxIn(masternodeOutpoint).ToString() + - boost::lexical_cast(nDenom) + - boost::lexical_cast(nTime) + - boost::lexical_cast(fReady); + std::to_string(nDenom) + + std::to_string(nTime) + + std::to_string(fReady); - if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternode.keyMasternode)) { + if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternodeInfo.legacyKeyOperator)) { LogPrintf("CDarksendQueue::Sign -- SignMessage() failed, %s\n", ToString()); return false; } - if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, vchSig, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(activeMasternodeInfo.legacyKeyIDOperator, vchSig, strMessage, strError)) { LogPrintf("CDarksendQueue::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -81,25 +88,33 @@ bool CDarksendQueue::Sign() return true; } -bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyMasternode) const +bool CDarksendQueue::CheckSignature(const CKeyID& keyIDOperator, const CBLSPublicKey& blsPubKey) const { std::string strError = ""; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + uint256 hash = GetSignatureHash(); - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + CBLSSignature sig; + sig.SetBuf(vchSig); + if (!sig.IsValid() || !sig.VerifyInsecure(blsPubKey, hash)) { + LogPrintf("CTxLockVote::CheckSignature -- VerifyInsecure() failed\n"); + return false; + } + } else if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyIDOperator, vchSig, strError)) { // we don't care about queues with old signature format LogPrintf("CDarksendQueue::CheckSignature -- VerifyHash() failed, error: %s\n", strError); return false; } } else { std::string strMessage = CTxIn(masternodeOutpoint).ToString() + - boost::lexical_cast(nDenom) + - boost::lexical_cast(nTime) + - boost::lexical_cast(fReady); + std::to_string(nDenom) + + std::to_string(nTime) + + std::to_string(fReady); - if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { LogPrintf("CDarksendQueue::CheckSignature -- Got bad Masternode queue signature: %s; error: %s\n", ToString(), strError); return false; } @@ -129,27 +144,32 @@ bool CDarksendBroadcastTx::Sign() std::string strError = ""; - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + uint256 hash = GetSignatureHash(); + + CBLSSignature sig = activeMasternodeInfo.blsKeyOperator->Sign(hash); + sig.GetBuf(vchSig); + } else if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::SignHash(hash, activeMasternode.keyMasternode, vchSig)) { + if (!CHashSigner::SignHash(hash, activeMasternodeInfo.legacyKeyOperator, vchSig)) { LogPrintf("CDarksendBroadcastTx::Sign -- SignHash() failed\n"); return false; } - if (!CHashSigner::VerifyHash(hash, activeMasternode.pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, activeMasternodeInfo.legacyKeyIDOperator, vchSig, strError)) { LogPrintf("CDarksendBroadcastTx::Sign -- VerifyHash() failed, error: %s\n", strError); return false; } } else { - std::string strMessage = tx->GetHash().ToString() + boost::lexical_cast(sigTime); + std::string strMessage = tx->GetHash().ToString() + std::to_string(sigTime); - if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternode.keyMasternode)) { + if(!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternodeInfo.legacyKeyOperator)) { LogPrintf("CDarksendBroadcastTx::Sign -- SignMessage() failed\n"); return false; } - if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, vchSig, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(activeMasternodeInfo.legacyKeyIDOperator, vchSig, strMessage, strError)) { LogPrintf("CDarksendBroadcastTx::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } @@ -158,22 +178,31 @@ bool CDarksendBroadcastTx::Sign() return true; } -bool CDarksendBroadcastTx::CheckSignature(const CPubKey& pubKeyMasternode) const +bool CDarksendBroadcastTx::CheckSignature(const CKeyID& keyIDOperator, const CBLSPublicKey& blsPubKey) const { std::string strError = ""; - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + uint256 hash = GetSignatureHash(); + + CBLSSignature sig; + sig.SetBuf(vchSig); + if (!sig.IsValid() || !sig.VerifyInsecure(blsPubKey, hash)) { + LogPrintf("CTxLockVote::CheckSignature -- VerifyInsecure() failed\n"); + return false; + } + } else if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + if (!CHashSigner::VerifyHash(hash, keyIDOperator, vchSig, strError)) { // we don't care about dstxes with old signature format LogPrintf("CDarksendBroadcastTx::CheckSignature -- VerifyHash() failed, error: %s\n", strError); return false; } } else { - std::string strMessage = tx->GetHash().ToString() + boost::lexical_cast(sigTime); + std::string strMessage = tx->GetHash().ToString() + std::to_string(sigTime); - if(!CMessageSigner::VerifyMessage(pubKeyMasternode, vchSig, strMessage, strError)) { + if(!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { LogPrintf("CDarksendBroadcastTx::CheckSignature -- Got bad dstx signature, error: %s\n", strError); return false; } @@ -188,35 +217,57 @@ bool CDarksendBroadcastTx::IsExpired(int nHeight) return (nConfirmedHeight != -1) && (nHeight - nConfirmedHeight > 24); } -void CPrivateSendBase::SetNull() +void CPrivateSendBaseSession::SetNull() { // Both sides + LOCK(cs_darksend); nState = POOL_STATE_IDLE; nSessionID = 0; nSessionDenom = 0; - nSessionInputCount = 0; vecEntries.clear(); finalMutableTransaction.vin.clear(); finalMutableTransaction.vout.clear(); nTimeLastSuccessfulStep = GetTime(); } -void CPrivateSendBase::CheckQueue() +void CPrivateSendBaseManager::SetNull() +{ + LOCK(cs_vecqueue); + vecDarksendQueue.clear(); +} + +void CPrivateSendBaseManager::CheckQueue() { - TRY_LOCK(cs_darksend, lockDS); + TRY_LOCK(cs_vecqueue, lockDS); if(!lockDS) return; // it's ok to fail here, we run this quite frequently // check mixing queue objects for timeouts std::vector::iterator it = vecDarksendQueue.begin(); while(it != vecDarksendQueue.end()) { if((*it).IsExpired()) { - LogPrint("privatesend", "CPrivateSendBase::%s -- Removing expired queue (%s)\n", __func__, (*it).ToString()); + LogPrint("privatesend", "CPrivateSendBaseManager::%s -- Removing expired queue (%s)\n", __func__, (*it).ToString()); it = vecDarksendQueue.erase(it); } else ++it; } } -std::string CPrivateSendBase::GetStateString() const +bool CPrivateSendBaseManager::GetQueueItemAndTry(CDarksendQueue& dsqRet) +{ + TRY_LOCK(cs_vecqueue, lockDS); + if(!lockDS) return false; // it's ok to fail here, we run this quite frequently + + for (auto& dsq : vecDarksendQueue) { + // only try each queue once + if(dsq.fTried || dsq.IsExpired()) continue; + dsq.fTried = true; + dsqRet = dsq; + return true; + } + + return false; +} + +std::string CPrivateSendBaseSession::GetStateString() const { switch(nState) { case POOL_STATE_IDLE: return "IDLE"; @@ -297,7 +348,7 @@ bool CPrivateSend::IsCollateralValid(const CTransaction& txCollateral) { LOCK(cs_main); CValidationState validationState; - if(!AcceptToMemoryPool(mempool, validationState, MakeTransactionRef(txCollateral), false, NULL, NULL, false, maxTxFee, true)) { + if(!AcceptToMemoryPool(mempool, validationState, MakeTransactionRef(txCollateral), false, NULL, false, maxTxFee, true)) { LogPrint("privatesend", "CPrivateSend::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n"); return false; } @@ -418,8 +469,8 @@ int CPrivateSend::GetDenominationsByAmounts(const std::vector& vecAmoun CScript scriptTmp = CScript(); std::vector vecTxOut; - BOOST_REVERSE_FOREACH(CAmount nAmount, vecAmount) { - CTxOut txout(nAmount, scriptTmp); + for (auto it = vecAmount.rbegin(); it != vecAmount.rend(); ++it) { + CTxOut txout((*it), scriptTmp); vecTxOut.push_back(txout); } @@ -459,7 +510,6 @@ std::string CPrivateSend::GetMessageByID(PoolMessage nMessageID) case MSG_NOERR: return _("No errors detected."); case MSG_SUCCESS: return _("Transaction created successfully."); case MSG_ENTRIES_ADDED: return _("Your entries added successfully."); - case ERR_INVALID_INPUT_COUNT: return _("Invalid input count."); default: return _("Unknown response."); } } @@ -509,60 +559,5 @@ void CPrivateSend::SyncTransaction(const CTransaction& tx, const CBlockIndex *pi // When tx is 0-confirmed or conflicted, posInBlock is SYNC_TRANSACTION_NOT_IN_BLOCK and nConfirmedHeight should be set to -1 mapDSTX[txHash].SetConfirmedHeight(posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK ? -1 : pindex->nHeight); - LogPrint("privatesend", "CPrivateSendClient::SyncTransaction -- txid=%s\n", txHash.ToString()); -} - -//TODO: Rename/move to core -void ThreadCheckPrivateSend(CConnman& connman) -{ - if(fLiteMode) return; // disable all Absolute specific functionality - - static bool fOneThread; - if(fOneThread) return; - fOneThread = true; - - // Make this thread recognisable as the PrivateSend thread - RenameThread("absolute-ps"); - - unsigned int nTick = 0; - - while (true) - { - MilliSleep(1000); - - // try to sync from all available nodes, one step at a time - masternodeSync.ProcessTick(connman); - - if(masternodeSync.IsBlockchainSynced() && !ShutdownRequested()) { - - nTick++; - - // make sure to check all masternodes first - mnodeman.Check(); - - mnodeman.ProcessPendingMnbRequests(connman); - mnodeman.ProcessPendingMnvRequests(connman); - - // check if we should activate or ping every few minutes, - // slightly postpone first run to give net thread a chance to connect to some peers - if(nTick % MASTERNODE_MIN_MNP_SECONDS == 15) - activeMasternode.ManageState(connman); - - if(nTick % 60 == 0) { - netfulfilledman.CheckAndRemove(); - mnodeman.ProcessMasternodeConnections(connman); - mnodeman.CheckAndRemove(connman); - mnodeman.WarnMasternodeDaemonUpdates(); - mnpayments.CheckAndRemove(); - instantsend.CheckAndRemove(); - } - if(fMasternodeMode && (nTick % (60 * 5) == 0)) { - mnodeman.DoFullVerificationStep(connman); - } - - if(nTick % (60 * 5) == 0) { - governance.DoMaintenance(connman); - } - } - } + LogPrint("privatesend", "CPrivateSend::SyncTransaction -- txid=%s\n", txHash.ToString()); } diff --git a/src/privatesend.h b/src/privatesend.h index ef5b135c0a6a..1897bb4e8c49 100644 --- a/src/privatesend.h +++ b/src/privatesend.h @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -13,6 +13,7 @@ #include "sync.h" #include "tinyformat.h" #include "timedata.h" +#include "bls/bls.h" class CPrivateSend; class CConnman; @@ -24,7 +25,7 @@ static const int PRIVATESEND_QUEUE_TIMEOUT = 30; static const int PRIVATESEND_SIGNING_TIMEOUT = 15; //! minimum peer version accepted by mixing pool -static const int MIN_PRIVATESEND_PEER_PROTO_VERSION = 70209; +static const int MIN_PRIVATESEND_PEER_PROTO_VERSION = 70211; static const size_t PRIVATESEND_ENTRY_MAX_SIZE = 9; @@ -52,7 +53,6 @@ enum PoolMessage { MSG_NOERR, MSG_SUCCESS, MSG_ENTRIES_ADDED, - ERR_INVALID_INPUT_COUNT, MSG_POOL_MIN = ERR_ALREADY_HAVE, MSG_POOL_MAX = MSG_ENTRIES_ADDED }; @@ -101,18 +101,15 @@ class CDarksendAccept { public: int nDenom; - int nInputCount; CMutableTransaction txCollateral; CDarksendAccept() : nDenom(0), - nInputCount(0), txCollateral(CMutableTransaction()) {}; - CDarksendAccept(int nDenom, int nInputCount, const CMutableTransaction& txCollateral) : + CDarksendAccept(int nDenom, const CMutableTransaction& txCollateral) : nDenom(nDenom), - nInputCount(nInputCount), txCollateral(txCollateral) {}; @@ -122,10 +119,9 @@ class CDarksendAccept inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(nDenom); int nVersion = s.GetVersion(); - if (nVersion > 70208) { + if (nVersion > 70208 && nVersion <= 70210) { + int nInputCount = 0; READWRITE(nInputCount); - } else if (ser_action.ForRead()) { - nInputCount = 0; } READWRITE(txCollateral); } @@ -180,7 +176,7 @@ class CDarksendQueue { public: int nDenom; - int nInputCount; + int nInputCount; // not used for anything but to calculate correct hash, remove after migration to 70211 COutPoint masternodeOutpoint; int64_t nTime; bool fReady; //ready for submit @@ -198,9 +194,9 @@ class CDarksendQueue fTried(false) {} - CDarksendQueue(int nDenom, int nInputCount, COutPoint outpoint, int64_t nTime, bool fReady) : + CDarksendQueue(int nDenom, COutPoint outpoint, int64_t nTime, bool fReady) : nDenom(nDenom), - nInputCount(nInputCount), + nInputCount(0), masternodeOutpoint(outpoint), nTime(nTime), fReady(fReady), @@ -214,25 +210,10 @@ class CDarksendQueue inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(nDenom); int nVersion = s.GetVersion(); - if (nVersion > 70208) { + if (nVersion > 70208 && nVersion <= 70210) { READWRITE(nInputCount); - } else if (ser_action.ForRead()) { - nInputCount = 0; - } - if (nVersion == 70208 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin{}; - if (ser_action.ForRead()) { - READWRITE(txin); - masternodeOutpoint = txin.prevout; - } else { - txin = CTxIn(masternodeOutpoint); - READWRITE(txin); - } - } else { - // using new format directly - READWRITE(masternodeOutpoint); } + READWRITE(masternodeOutpoint); READWRITE(nTime); READWRITE(fReady); if (!(s.GetType() & SER_GETHASH)) { @@ -250,7 +231,7 @@ class CDarksendQueue */ bool Sign(); /// Check if we have a valid Masternode address - bool CheckSignature(const CPubKey& pubKeyMasternode) const; + bool CheckSignature(const CKeyID& keyIDOperator, const CBLSPublicKey& blsPubKey) const; bool Relay(CConnman &connman); @@ -259,13 +240,13 @@ class CDarksendQueue std::string ToString() const { - return strprintf("nDenom=%d, nInputCount=%d, nTime=%lld, fReady=%s, fTried=%s, masternode=%s", - nDenom, nInputCount, nTime, fReady ? "true" : "false", fTried ? "true" : "false", masternodeOutpoint.ToStringShort()); + return strprintf("nDenom=%d, nTime=%lld, fReady=%s, fTried=%s, masternode=%s", + nDenom, nTime, fReady ? "true" : "false", fTried ? "true" : "false", masternodeOutpoint.ToStringShort()); } friend bool operator==(const CDarksendQueue& a, const CDarksendQueue& b) { - return a.nDenom == b.nDenom && a.nInputCount == b.nInputCount && a.masternodeOutpoint == b.masternodeOutpoint && a.nTime == b.nTime && a.fReady == b.fReady; + return a.nDenom == b.nDenom && a.masternodeOutpoint == b.masternodeOutpoint && a.nTime == b.nTime && a.fReady == b.fReady; } }; @@ -305,21 +286,7 @@ class CDarksendBroadcastTx template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(tx); - int nVersion = s.GetVersion(); - if (nVersion == 70208 && (s.GetType() & SER_NETWORK)) { - // converting from/to old format - CTxIn txin{}; - if (ser_action.ForRead()) { - READWRITE(txin); - masternodeOutpoint = txin.prevout; - } else { - txin = CTxIn(masternodeOutpoint); - READWRITE(txin); - } - } else { - // using new format directly - READWRITE(masternodeOutpoint); - } + READWRITE(masternodeOutpoint); if (!(s.GetType() & SER_GETHASH)) { READWRITE(vchSig); } @@ -342,21 +309,18 @@ class CDarksendBroadcastTx uint256 GetSignatureHash() const; bool Sign(); - bool CheckSignature(const CPubKey& pubKeyMasternode) const; + bool CheckSignature(const CKeyID& keyIDOperator, const CBLSPublicKey& blsPubKey) const; void SetConfirmedHeight(int nConfirmedHeightIn) { nConfirmedHeight = nConfirmedHeightIn; } bool IsExpired(int nHeight); }; // base class -class CPrivateSendBase +class CPrivateSendBaseSession { protected: mutable CCriticalSection cs_darksend; - // The current mixing sessions in progress on the network - std::vector vecDarksendQueue; - std::vector vecEntries; // Masternode/clients entries PoolState nState; // should be one of the POOL_STATE_XXX values @@ -367,21 +331,44 @@ class CPrivateSendBase CMutableTransaction finalMutableTransaction; // the finalized transaction ready for signing void SetNull(); - void CheckQueue(); public: - int nSessionDenom; //Users must submit an denom matching this - int nSessionInputCount; //Users must submit a count matching this - - CPrivateSendBase() { SetNull(); } + int nSessionDenom; //Users must submit a denom matching this + + CPrivateSendBaseSession() : + vecEntries(), + nState(POOL_STATE_IDLE), + nTimeLastSuccessfulStep(0), + nSessionID(0), + finalMutableTransaction(), + nSessionDenom(0) + {} - int GetQueueSize() const { return vecDarksendQueue.size(); } int GetState() const { return nState; } std::string GetStateString() const; int GetEntriesCount() const { return vecEntries.size(); } }; +// base class +class CPrivateSendBaseManager +{ +protected: + mutable CCriticalSection cs_vecqueue; + + // The current mixing sessions in progress on the network + std::vector vecDarksendQueue; + + void SetNull(); + void CheckQueue(); + +public: + CPrivateSendBaseManager() : vecDarksendQueue() {} + + int GetQueueSize() const { return vecDarksendQueue.size(); } + bool GetQueueItemAndTry(CDarksendQueue& dsqRet); +}; + // helper class class CPrivateSend { @@ -438,6 +425,4 @@ class CPrivateSend static void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); }; -void ThreadCheckPrivateSend(CConnman& connman); - #endif diff --git a/src/protocol.cpp b/src/protocol.cpp index 4e832c27aac9..e55c25b55bc0 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -39,7 +39,7 @@ const char *SENDCMPCT="sendcmpct"; const char *CMPCTBLOCK="cmpctblock"; const char *GETBLOCKTXN="getblocktxn"; const char *BLOCKTXN="blocktxn"; -// Dash message types +// Absolute message types const char *TXLOCKREQUEST="ix"; const char *TXLOCKVOTE="txlvote"; const char *SPORK="spork"; @@ -69,6 +69,8 @@ const char *MNGOVERNANCESYNC="govsync"; const char *MNGOVERNANCEOBJECT="govobj"; const char *MNGOVERNANCEOBJECTVOTE="govobjvote"; const char *MNVERIFY="mnv"; +const char *GETMNLISTDIFF="getmnlistd"; +const char *MNLISTDIFF="mnlistdiff"; }; static const char* ppszTypeName[] = @@ -128,7 +130,7 @@ const static std::string allNetMessageTypes[] = { NetMsgType::CMPCTBLOCK, NetMsgType::GETBLOCKTXN, NetMsgType::BLOCKTXN, - // Dash message types + // Absolute message types // NOTE: do NOT include non-implmented here, we want them to be "Unknown command" in ProcessMessage() NetMsgType::TXLOCKREQUEST, NetMsgType::TXLOCKVOTE, @@ -153,6 +155,8 @@ const static std::string allNetMessageTypes[] = { NetMsgType::MNGOVERNANCEOBJECT, NetMsgType::MNGOVERNANCEOBJECTVOTE, NetMsgType::MNVERIFY, + NetMsgType::GETMNLISTDIFF, + NetMsgType::MNLISTDIFF, }; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index 0773fa68115a..30bb43c07e09 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -244,7 +244,7 @@ extern const char *GETBLOCKTXN; */ extern const char *BLOCKTXN; -// Dash message types +// Absolute message types // NOTE: do NOT declare non-implmented here, we don't want them to be exposed to the outside // TODO: add description extern const char *TXLOCKREQUEST; @@ -269,6 +269,8 @@ extern const char *MNGOVERNANCESYNC; extern const char *MNGOVERNANCEOBJECT; extern const char *MNGOVERNANCEOBJECTVOTE; extern const char *MNVERIFY; +extern const char *GETMNLISTDIFF; +extern const char *MNLISTDIFF; }; /* Get a vector of all valid message types (see above) */ diff --git a/src/qt/absolute.cpp b/src/qt/absolute.cpp index c990e40c1515..057ec189ec48 100644 --- a/src/qt/absolute.cpp +++ b/src/qt/absolute.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/absolutestrings.cpp b/src/qt/absolutestrings.cpp index 940560805074..49b4ccf82afb 100644 --- a/src/qt/absolutestrings.cpp +++ b/src/qt/absolutestrings.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2013-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-2020 The Dash Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index e23101be2bea..533ff111b981 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 276395d9488d..d8cc1994b12a 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index d49245cef87d..5672459a5f48 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index b331f54ef043..dbf1b2c4cd91 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2014 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 795a35fb0fee..62180e9238a4 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 2e22fc0fa5a8..b2b8e804dcca 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index d2221c9712bd..e4622cde0cf2 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index eed14cc99144..df3a4d4a5c62 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index fb8f0b311b54..43a54558ca13 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 7184ffff4531..9caa9d32a22e 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -434,10 +434,10 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); else { coinControl->Select(outpt); - int nRounds = pwalletMain->GetOutpointPrivateSendRounds(outpt); + int nRounds = pwalletMain->GetRealOutpointPrivateSendRounds(outpt); if (coinControl->fUsePrivateSend && nRounds < privateSendClient.nPrivateSendRounds) { QMessageBox::warning(this, windowTitle(), - tr("Non-anonymized input selected. PrivateSend will be disabled.

If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again."), + tr("Non-anonymized input selected. PrivateSend will be disabled.

If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again."), QMessageBox::Ok, QMessageBox::Ok); coinControl->fUsePrivateSend = false; } @@ -570,7 +570,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nPayFee = coinControl->nMinimumTotalFee; // InstantSend Fee - if (coinControl->fUseInstantSend) nPayFee = std::max(nPayFee, CTxLockRequest(txDummy).GetMinFee()); + if (coinControl->fUseInstantSend) nPayFee = std::max(nPayFee, CTxLockRequest(txDummy).GetMinFee(true)); // Allow free? (require at least hard-coded threshold and default to that if no estimate) double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget); @@ -782,7 +782,7 @@ void CoinControlDialog::updateView() // PrivateSend rounds COutPoint outpoint = COutPoint(out.tx->tx->GetHash(), out.i); - int nRounds = pwalletMain->GetOutpointPrivateSendRounds(outpoint); + int nRounds = pwalletMain->GetRealOutpointPrivateSendRounds(outpoint); if (nRounds >= 0 || fDebug) itemOutput->setText(COLUMN_PRIVATESEND_ROUNDS, QString::number(nRounds)); else itemOutput->setText(COLUMN_PRIVATESEND_ROUNDS, tr("n/a")); diff --git a/src/qt/darksendconfig.h b/src/qt/darksendconfig.h deleted file mode 100644 index a1e1a37c5ef6..000000000000 --- a/src/qt/darksendconfig.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef DARKSENDCONFIG_H -#define DARKSENDCONFIG_H - -#include - -namespace Ui { - class DarksendConfig; -} -class WalletModel; - -/** Multifunctional dialog to ask for passphrases. Used for encryption, unlocking, and changing the passphrase. - */ -class DarksendConfig : public QDialog -{ - Q_OBJECT - -public: - - DarksendConfig(QWidget *parent = 0); - ~DarksendConfig(); - - void setModel(WalletModel *model); - - -private: - Ui::DarksendConfig *ui; - WalletModel *model; - void configure(bool enabled, int coins, int rounds); - -private Q_SLOTS: - - void clickBasic(); - void clickHigh(); - void clickMax(); -}; - -#endif // DARKSENDCONFIG_H diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index 37f254e4d16c..4ae0b8aa7d5c 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2014 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 562d015794a1..859d509a953c 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -178,6 +178,16 @@ + + + + Show system popups for PrivateSend mixing transactions<br/>just like for all other transaction types. + + + Show popups for PrivateSend transactions + + + @@ -229,7 +239,7 @@ 2 - 8 + 16 4 diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index e07041f871ae..3cd79e66b54a 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -35,6 +35,8 @@ static const bool DEFAULT_SPLASHSCREEN = true; #define COLOR_TX_STATUS_DANGER QColor(200, 100, 100) /* Transaction list -- TX status decoration - default color */ #define COLOR_BLACK QColor(0, 0, 0) +/* Transaction list -- TX status decoration - LockedByInstantSend color */ +#define COLOR_TX_STATUS_LOCKED QColor(0, 128, 255) /* Tooltips longer than this (in characters) are converted into rich text, so that they can be word-wrapped. diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index d47316c130b4..21cd52118443 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 1ade0ced4f20..0622a39e9772 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp index 3fec1b433ef5..ab375dcbe806 100644 --- a/src/qt/masternodelist.cpp +++ b/src/qt/masternodelist.cpp @@ -213,8 +213,9 @@ void MasternodeList::updateMyMasternodeInfo(QString strAlias, QString strAddr, c QTableWidgetItem *protocolItem = new QTableWidgetItem(QString::number(fFound ? infoMn.nProtocolVersion : -1)); QTableWidgetItem *statusItem = new QTableWidgetItem(QString::fromStdString(fFound ? CMasternode::StateToString(infoMn.nActiveState) : "MISSING")); QTableWidgetItem *activeSecondsItem = new QTableWidgetItem(QString::fromStdString(DurationToDHMS(fFound ? (infoMn.nTimeLastPing - infoMn.sigTime) : 0))); - QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(fFound ? DateTimeStrFormat("%Y-%m-%d %H:%M", infoMn.nTimeLastPing + GetOffsetFromUtc()) : "")); - QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(fFound ? CBitcoinAddress(infoMn.pubKeyCollateralAddress.GetID()).ToString() : "")); + QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M", + fFound ? infoMn.nTimeLastPing + GetOffsetFromUtc() : 0))); + QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(fFound ? CBitcoinAddress(infoMn.keyIDCollateralAddress).ToString() : "")); ui->tableWidgetMyMasternodes->setItem(nNewRow, 0, aliasItem); ui->tableWidgetMyMasternodes->setItem(nNewRow, 1, addrItem); @@ -301,7 +302,7 @@ void MasternodeList::updateNodeList() QTableWidgetItem *statusItem = new QTableWidgetItem(QString::fromStdString(mn.GetStatus())); QTableWidgetItem *activeSecondsItem = new QTableWidgetItem(QString::fromStdString(DurationToDHMS(mn.lastPing.sigTime - mn.sigTime))); QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M", mn.lastPing.sigTime + offsetFromUtc))); - QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString())); + QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(CBitcoinAddress(mn.keyIDCollateralAddress).ToString())); if (strCurrentFilter != "") { @@ -473,10 +474,7 @@ void MasternodeList::ShowQRCode(std::string strAlias) { bool fFound = false; for (const auto& mne : masternodeConfig.getEntries()) { - if (strAlias != mne.getAlias()) { - continue; - } - else { + if (strAlias == mne.getAlias()) { strMNPrivKey = mne.getPrivKey(); strCollateral = mne.getTxHash() + "-" + mne.getOutputIndex(); strIP = mne.getIp(); diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index 2a7d69b96e5a..3e13ed818c9d 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2014 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/networkstyle.h b/src/qt/networkstyle.h index 0f602e75e532..affee511903a 100644 --- a/src/qt/networkstyle.h +++ b/src/qt/networkstyle.h @@ -1,6 +1,6 @@ // Copyright (c) 2014 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 4cfbd8bf69bf..f91353315fe5 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2014 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 96fd5437539b..0a696935f354 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 88d3e27c9fca..8a2548f1b3df 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -20,7 +20,6 @@ #include "walletmodel.h" #include "instantx.h" -#include "darksendconfig.h" #include "masternode-sync.h" #include "privatesend-client.h" @@ -566,7 +565,7 @@ void OverviewPage::privateSendStatus() updatePrivateSendProgress(); } - QString strStatus = QString(privateSendClient.GetStatus().c_str()); + QString strStatus = QString(privateSendClient.GetStatuses().c_str()); QString s = tr("Last PrivateSend message:\n") + strStatus; @@ -575,13 +574,7 @@ void OverviewPage::privateSendStatus() ui->labelPrivateSendLastMessage->setText(s); - if(privateSendClient.nSessionDenom == 0){ - ui->labelSubmittedDenom->setText(tr("N/A")); - } else { - QString strDenom(CPrivateSend::GetDenominationsToString(privateSendClient.nSessionDenom).c_str()); - ui->labelSubmittedDenom->setText(strDenom); - } - + ui->labelSubmittedDenom->setText(QString(privateSendClient.GetSessionDenoms().c_str())); } void OverviewPage::privateSendAuto(){ @@ -644,18 +637,9 @@ void OverviewPage::togglePrivateSend(){ if(!privateSendClient.fEnablePrivateSend){ ui->togglePrivateSend->setText(tr("Start Mixing")); - privateSendClient.UnlockCoins(); + privateSendClient.ResetPool(); } else { ui->togglePrivateSend->setText(tr("Stop Mixing")); - - /* show darksend configuration if client has defaults set */ - - if(privateSendClient.nPrivateSendAmount == 0){ - DarksendConfig dlg(this); - dlg.setModel(walletModel); - dlg.exec(); - } - } } diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 4e02cdac9933..e4e11c73fead 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/res/images/base/wallet_version.png b/src/qt/res/images/base/wallet_version.png index 6c61de7bfc5a..1047c7b4397f 100644 Binary files a/src/qt/res/images/base/wallet_version.png and b/src/qt/res/images/base/wallet_version.png differ diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 0ef8758c6b16..1a522cb2ae01 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 767db851eb8b..7ccb1b82e7d5 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index c0f79d8b3f87..0be802665e3b 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index a78c87fd2b0f..e2f3ec5b5b62 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 6acb822c31bf..c6bb7981a63e 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 407f0f4544e9..eeba5d943926 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -14,6 +14,8 @@ #include "univalue.h" #include "util.h" +#include "evo/deterministicmns.h" + #include #include @@ -42,13 +44,15 @@ void RPCNestedTests::rpcNestedTests() RegisterAllCoreRPCCommands(tableRPC); tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); ClearDatadirCache(); - std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); + std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_dash_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); QDir dir(QString::fromStdString(path)); dir.mkpath("."); ForceSetArg("-datadir", path); //mempool.setSanityCheck(1.0); + evoDb = new CEvoDB(1 << 20, true, true); pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); + deterministicMNManager = new CDeterministicMNManager(*evoDb); pcoinsTip = new CCoinsViewCache(pcoinsdbview); InitBlockIndex(chainparams); { diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index edd6cd9ea41b..84f7f9c59df3 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 85cc916eeee2..6318cc6c9681 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 0ca99ec787eb..569d134b472e 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -147,14 +147,24 @@ QList TransactionRecord::decomposeTransaction(const CWallet * } else { - for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++) + sub.idx = parts.size(); + if(wtx.tx->vin.size() == 1 && wtx.tx->vout.size() == 1 + && CPrivateSend::IsCollateralAmount(nDebit) + && CPrivateSend::IsCollateralAmount(nCredit) + && CPrivateSend::IsCollateralAmount(-nNet)) { - const CTxOut& txout = wtx.tx->vout[nOut]; - sub.idx = parts.size(); - - if(txout.nValue == CPrivateSend::GetMaxCollateralAmount()) sub.type = TransactionRecord::PrivateSendMakeCollaterals; - if(CPrivateSend::IsDenominatedAmount(txout.nValue)) sub.type = TransactionRecord::PrivateSendCreateDenominations; - if(nDebit - wtx.tx->GetValueOut() == CPrivateSend::GetCollateralAmount()) sub.type = TransactionRecord::PrivateSendCollateralPayment; + sub.type = TransactionRecord::PrivateSendCollateralPayment; + } else { + for (const auto& txout : wtx.tx->vout) { + if(txout.nValue == CPrivateSend::GetMaxCollateralAmount()) { + sub.type = TransactionRecord::PrivateSendMakeCollaterals; + break; + } + if(CPrivateSend::IsDenominatedAmount(txout.nValue)) { + sub.type = TransactionRecord::PrivateSendCreateDenominations; + break; + } + } } } @@ -172,7 +182,21 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // CAmount nTxFee = nDebit - wtx.tx->GetValueOut(); - for (unsigned int nOut = 0; nOut < wtx.tx->vout.size(); nOut++) + bool fDone = false; + if(wtx.tx->vin.size() == 1 && wtx.tx->vout.size() == 1 + && CPrivateSend::IsCollateralAmount(nDebit) + && nCredit == 0 // OP_RETURN + && CPrivateSend::IsCollateralAmount(-nNet)) + { + TransactionRecord sub(hash, nTime); + sub.idx = 0; + sub.type = TransactionRecord::PrivateSendCollateralPayment; + sub.debit = -nDebit; + parts.append(sub); + fDone = true; + } + + for (unsigned int nOut = 0; nOut < wtx.tx->vout.size() && !fDone; nOut++) { const CTxOut& txout = wtx.tx->vout[nOut]; TransactionRecord sub(hash, nTime); @@ -292,6 +316,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) } else { + status.lockedByInstantSend = wtx.IsLockedByInstantSend(); if (status.depth < 0) { status.status = TransactionStatus::Conflicted; diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 0dca6d2419fa..a78c882ad930 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -20,7 +20,7 @@ class TransactionStatus { public: TransactionStatus(): - countsForBalance(false), sortKey(""), + countsForBalance(false), lockedByInstantSend(false), sortKey(""), matures_in(0), status(Offline), depth(0), open_for(0), cur_num_blocks(-1) { } @@ -42,6 +42,8 @@ class TransactionStatus /// Transaction counts towards available balance bool countsForBalance; + /// Transaction was locked via InstantSend + bool lockedByInstantSend; /// Sorting key based on status std::string sortKey; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index d24087aca6de..db16222d9149 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -484,6 +484,24 @@ QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool return QString(str); } +QIcon IconWithShiftedColor(const QString& filename, bool shift) +{ + if (!shift) + return QIcon(filename); + + QImage img(filename); + img = img.convertToFormat(QImage::Format_ARGB32); + for (int x = img.width(); x--; ) + { + for (int y = img.height(); y--; ) + { + const QRgb rgb = img.pixel(x, y); + img.setPixel(x, y, qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb)+128, qAlpha(rgb))); + } + } + return QIcon(QPixmap::fromImage(img)); +} + QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const { QString theme = GUIUtil::getThemeName(); @@ -495,17 +513,19 @@ QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) case TransactionStatus::Offline: return COLOR_TX_STATUS_OFFLINE; case TransactionStatus::Unconfirmed: + // TODO: use special icon for InstantSend 0-conf return QIcon(":/icons/" + theme + "/transaction_0"); case TransactionStatus::Abandoned: return QIcon(":/icons/" + theme + "/transaction_abandoned"); case TransactionStatus::Confirming: switch(wtx->status.depth) { - case 1: return QIcon(":/icons/" + theme + "/transaction_1"); - case 2: return QIcon(":/icons/" + theme + "/transaction_2"); - case 3: return QIcon(":/icons/" + theme + "/transaction_3"); - case 4: return QIcon(":/icons/" + theme + "/transaction_4"); - default: return QIcon(":/icons/" + theme + "/transaction_5"); + // TODO: use special icons for InstantSend instead of color shifting + case 1: return IconWithShiftedColor(":/icons/" + theme + "/transaction_1", wtx->status.lockedByInstantSend); + case 2: return IconWithShiftedColor(":/icons/" + theme + "/transaction_2", wtx->status.lockedByInstantSend); + case 3: return IconWithShiftedColor(":/icons/" + theme + "/transaction_3", wtx->status.lockedByInstantSend); + case 4: return IconWithShiftedColor(":/icons/" + theme + "/transaction_4", wtx->status.lockedByInstantSend); + default: return IconWithShiftedColor(":/icons/" + theme + "/transaction_5", wtx->status.lockedByInstantSend); }; case TransactionStatus::Confirmed: return QIcon(":/icons/" + theme + "/transaction_confirmed"); @@ -608,6 +628,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const { return COLOR_TX_STATUS_DANGER; } + if(rec->status.lockedByInstantSend) + { + return COLOR_TX_STATUS_LOCKED; + } // Non-confirmed (but not immature) as transactions are grey if(!rec->status.countsForBalance && rec->status.status != TransactionStatus::Immature) { diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index e2e6a10741c1..e5bc6e988576 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 168def534fc3..5cd84686d7a7 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2011-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -757,7 +757,7 @@ bool WalletModel::transactionCanBeAbandoned(uint256 hash) const { LOCK2(cs_main, wallet->cs_wallet); const CWalletTx *wtx = wallet->GetWalletTx(hash); - if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->InMempool()) + if (!wtx || wtx->isAbandoned() || wtx->GetDepthInMainChain() > 0 || wtx->IsLockedByInstantSend() || wtx->InMempool()) return false; return true; } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index fe6162d34fc7..7c13abb1cbef 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -24,6 +24,9 @@ #include "utilstrencodings.h" #include "hash.h" +#include "evo/specialtx.h" +#include "evo/cbtx.h" + #include #include @@ -131,6 +134,14 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx txs.push_back(tx->GetHash().GetHex()); } result.push_back(Pair("tx", txs)); + if (!block.vtx[0]->vExtraPayload.empty()) { + CCbTx cbTx; + if (GetTxPayload(block.vtx[0]->vExtraPayload, cbTx)) { + UniValue cbTxObj; + cbTx.ToJson(cbTxObj); + result.push_back(Pair("cbTx", cbTxObj)); + } + } result.push_back(Pair("time", block.GetBlockTime())); result.push_back(Pair("mediantime", (int64_t)blockindex->GetMedianTimePast())); result.push_back(Pair("nonce", (uint64_t)block.nNonce)); @@ -1283,7 +1294,9 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); BIP9SoftForkDescPushBack(bip9_softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV); BIP9SoftForkDescPushBack(bip9_softforks, "dip0001", consensusParams, Consensus::DEPLOYMENT_DIP0001); + BIP9SoftForkDescPushBack(bip9_softforks, "aip0003", consensusParams, Consensus::DEPLOYMENT_AIP0003); BIP9SoftForkDescPushBack(bip9_softforks, "bip147", consensusParams, Consensus::DEPLOYMENT_BIP147); + BIP9SoftForkDescPushBack(bip9_softforks, "autoix", consensusParams, Consensus::DEPLOYMENT_ISAUTOLOCKS); obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index df5a9d3445ea..58d7940034d6 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2015 The Bitcoin Core developers -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -140,6 +140,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "setban", 3, "absolute" }, { "setbip69enabled", 0, "enabled" }, { "setnetworkactive", 0, "state" }, + { "setprivatesendrounds", 0, "rounds" }, + { "setprivatesendamount", 0, "amount" }, { "getmempoolancestors", 1, "verbose" }, { "getmempooldescendants", 1, "verbose" }, { "spork", 1, "value" }, diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 2940fe4016d7..7da400993cdd 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -1,5 +1,5 @@ -// Copyright (c) 2014-2017 The Dash Core developers -// Copyright (c) 2018 The Absolute Core developers +// Copyright (c) 2014-2020 The Dash Core developers +// Copyright (c) 2018-2020 The Absolute Core developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -27,839 +27,1009 @@ bool EnsureWalletIsAvailable(bool avoidException); -UniValue gobject(const JSONRPCRequest& request) +void gobject_count_help() { - std::string strCommand; - if (request.params.size() >= 1) - strCommand = request.params[0].get_str(); - - if (request.fHelp || - ( -#ifdef ENABLE_WALLET - strCommand != "prepare" && -#endif // ENABLE_WALLET - strCommand != "vote-many" && strCommand != "vote-conf" && strCommand != "vote-alias" && strCommand != "submit" && strCommand != "count" && - strCommand != "deserialize" && strCommand != "get" && strCommand != "getvotes" && strCommand != "getcurrentvotes" && strCommand != "list" && strCommand != "diff" && - strCommand != "check" )) - throw std::runtime_error( - "gobject \"command\"...\n" - "Manage governance objects\n" - "\nAvailable commands:\n" - " check - Validate governance object data (proposal only)\n" -#ifdef ENABLE_WALLET - " prepare - Prepare governance object by signing and creating tx\n" -#endif // ENABLE_WALLET - " submit - Submit governance object to network\n" - " deserialize - Deserialize governance object from hex string to JSON\n" - " count - Count governance objects and votes (additional param: 'json' or 'all', default: 'json')\n" - " get - Get governance object by hash\n" - " getvotes - Get all votes for a governance object hash (including old votes)\n" - " getcurrentvotes - Get only current (tallying) votes for a governance object hash (does not include old votes)\n" - " list - List governance objects (can be filtered by signal and/or object type)\n" - " diff - List differences since last diff\n" - " vote-alias - Vote on a governance object by masternode alias (using masternode.conf setup)\n" - " vote-conf - Vote on a governance object by masternode configured in absolute.conf\n" - " vote-many - Vote on a governance object by all masternodes (using masternode.conf setup)\n" + throw std::runtime_error( + "gobject count (\"mode\")\n" + "Count governance objects and votes\n" + "\nArguments:\n" + "1. \"mode\" (string, optional, default: \"json\") Output format: json (\"json\") or string in free form (\"all\")\n" ); +} +UniValue gobject_count(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() > 2) + gobject_count_help(); - if(strCommand == "count") { - std::string strMode{"json"}; - - if (request.params.size() == 2) { - strMode = request.params[1].get_str(); - } - - if (request.params.size() > 2 || (strMode != "json" && strMode != "all")) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject count ( \"json\"|\"all\" )'"); - } + std::string strMode{"json"}; - return strMode == "json" ? governance.ToJson() : governance.ToString(); + if (request.params.size() == 2) { + strMode = request.params[1].get_str(); } - /* - ------ Example Governance Item ------ - gobject submit 6e622bb41bad1fb18e7f23ae96770aeb33129e18bd9efe790522488e580a0a03 0 1 1464292854 "beer-reimbursement" 5b5b22636f6e7472616374222c207b2270726f6a6563745f6e616d65223a20225c22626565722d7265696d62757273656d656e745c22222c20227061796d656e745f61646472657373223a20225c225879324c4b4a4a64655178657948726e34744744514238626a6876464564615576375c22222c2022656e645f64617465223a202231343936333030343030222c20226465736372697074696f6e5f75726c223a20225c227777772e646173687768616c652e6f72672f702f626565722d7265696d62757273656d656e745c22222c2022636f6e74726163745f75726c223a20225c22626565722d7265696d62757273656d656e742e636f6d2f3030312e7064665c22222c20227061796d656e745f616d6f756e74223a20223233342e323334323232222c2022676f7665726e616e63655f6f626a6563745f6964223a2037342c202273746172745f64617465223a202231343833323534303030227d5d5d1 - */ + if (strMode != "json" && strMode != "all") + gobject_count_help(); - // DEBUG : TEST DESERIALIZATION OF GOVERNANCE META DATA - if(strCommand == "deserialize") - { - if (request.params.size() != 2) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject deserialize '"); - } + return strMode == "json" ? governance.ToJson() : governance.ToString(); +} - std::string strHex = request.params[1].get_str(); +void gobject_deserialize_help() +{ + throw std::runtime_error( + "gobject deserialize \"hex_data\"\n" + "Deserialize governance object from hex string to JSON\n" + "\nArguments:\n" + "1. \"hex_data\" (string, required) data in hex string form\n" + ); +} - std::vector v = ParseHex(strHex); - std::string s(v.begin(), v.end()); +UniValue gobject_deserialize(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 2) + gobject_deserialize_help(); - UniValue u(UniValue::VOBJ); - u.read(s); + std::string strHex = request.params[1].get_str(); - return u.write().c_str(); - } + std::vector v = ParseHex(strHex); + std::string s(v.begin(), v.end()); - // VALIDATE A GOVERNANCE OBJECT PRIOR TO SUBMISSION - if(strCommand == "check") - { - if (request.params.size() != 2) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject check '"); - } + UniValue u(UniValue::VOBJ); + u.read(s); - // ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS + return u.write().c_str(); +} - uint256 hashParent; +void gobject_check_help() +{ + throw std::runtime_error( + "gobject check \"hex_data\"\n" + "Validate governance object data (proposal only)\n" + "\nArguments:\n" + "1. \"hex_data\" (string, required) data in hex string form\n" + ); +} - int nRevision = 1; +UniValue gobject_check(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 2) + gobject_check_help(); - int64_t nTime = GetAdjustedTime(); - std::string strDataHex = request.params[1].get_str(); + // ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS - CGovernanceObject govobj(hashParent, nRevision, nTime, uint256(), strDataHex); + uint256 hashParent; - if(govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { - CProposalValidator validator(strDataHex); - if(!validator.Validate()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); - } - } - else { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid object type, only proposals can be validated"); - } + int nRevision = 1; - UniValue objResult(UniValue::VOBJ); + int64_t nTime = GetAdjustedTime(); + std::string strDataHex = request.params[1].get_str(); - objResult.push_back(Pair("Object status", "OK")); + CGovernanceObject govobj(hashParent, nRevision, nTime, uint256(), strDataHex); - return objResult; + if (govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { + CProposalValidator validator(strDataHex); + if (!validator.Validate()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); + } + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid object type, only proposals can be validated"); } -#ifdef ENABLE_WALLET - // PREPARE THE GOVERNANCE OBJECT BY CREATING A COLLATERAL TRANSACTION - if(strCommand == "prepare") - { - if (!EnsureWalletIsAvailable(request.fHelp)) - return NullUniValue; - - if (request.params.size() != 5) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject prepare